gazand
gazand

Reputation: 19

Weird narrowing conversion double to float warnings in g++ compiler

I have this function for creating a vector from polar notation which uses braced initializer in it:

// Constructs a 2D vector from XY-coordinates.
inline vector2(float x, float y) : x_(x), y_(y) { }

// Constructs a 2D vector from polar coordinates
static vector2 polar(float r, float phi) {
    return {r * cos(phi), r * sin(phi)};
}

In MSVS all seems fine, but g++ compiler shows warnings that for me seems rather weird:

vector2.h:37:23: warning: narrowing conversion of ‘(((double)r) * cos(((double)phi)))’ from ‘double’ to ‘float’ inside { } [-Wnarrowing]
             return {r * cos(phi), r * sin(phi)};
                     ~~^~~~~~~~~~

If I use constructor warning disappears:

// Constructs a 2D vector from polar coordinates
static vector2 polar(float r, float phi) {
    return vector2(r * cos(phi), r * sin(phi));
}

Why does this warning appear? Does it mean that a compiled program will do an unnecessary conversion from float to double and back to float?

UPDATE Here is minimal reproducible example

#include <iostream>
#include <cmath>

using std::cout;
using std::endl;

class Pair {
public:
    float x_;
    float y_;

    inline Pair(float x, float y) : x_(x), y_(y) {};

};

Pair braced(float a) {
    return {a * 2, cos(a) * 3};
}

Pair constr(float a) {
    return Pair(a * 2, cos(a) * 3);
}

Pair stdbraced(float a) {
    return {a * 2, std::cos(a) * 3};
}

Pair stdconstr(float a) {
    return Pair(a * 2, std::cos(a) * 3);
}

int main() {
    float x = 2.0;
    auto a = braced(x);
    cout << a.x_ << ' ' << a.y_ << endl;
    auto b = constr(x);
    cout << b.x_ << ' ' << b.y_ << endl;
    auto c = stdbraced(x);
    cout << c.x_ << ' ' << c.y_ << endl;
    auto d = stdconstr(x);
    cout << d.x_ << ' ' << d.y_ << endl;
}

Output of g++ test.cpp -o test:

test.cpp: In function ‘Pair braced(float)’:
test.cpp:15:27: warning: narrowing conversion of ‘(cos(((double)a)) * (double)3)’ from ‘double’ to ‘float’ inside { } [-Wnarrowing]
         return {a*2,cos(a)*3};
                     ~~~~~~^~

So using std::cos do help. But the main question remains (and bothering me) - why warning appears only when using braced initialization?

Upvotes: 1

Views: 1014

Answers (2)

Surt
Surt

Reputation: 16089

You are not using the correct cos and sin

#include <cmath>

class vector2 {
public:
float x_, y_;
// Constructs a 2D vector from XY-coordinates.
inline vector2(float x, float y) : x_(x), y_(y) { }
};

// Constructs a 2D vector from polar coordinates
static vector2 polar(float r, float phi) {
    return {r * std::cos(phi), r * std::sin(phi)};
}

Using the std::cos and std::sin gives the correct result as they are overload like this:

double cos (double x);
float cos (float x);
long double cos (long double x);
double cos (T x);           // additional overloads for integral types

Edit: Running it in compiler explorer Just give the same warning, but pressing the cppinsights button gives the more correct error:

/home/insights/insights.cpp:17:20: error: non-constant-expression cannot be narrowed from type 'double' to 'float' in initializer list [-Wc++11-narrowing] return {a * 2, cos(a) * 3}; ^~~~~~~~~~ /home/insights/insights.cpp:17:20: note: insert an explicit cast to silence this issue return {a * 2, cos(a) * 3}; ^~~~~~~~~~

Which then lead to the definition of initializer list which mentions list-initialize and the subsection "narrowing"

Narrowing conversions list-initialization limits the allowed implicit conversions by prohibiting the following:

  • conversion from a floating-point type to an integer type
  • conversion from a long double to double or to float and conversion from double to float, except where the source is a constant expression and overflow does not occur
  • conversion from an integer type to a floating-point type, except where the source is a constant expression whose value can be stored exactly in the target type
  • conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source is a constant expression whose value can be stored exactly in the target type

So if I read that correctly it should have been an error.

Upvotes: 1

Nopileos
Nopileos

Reputation: 2097

It would be helpful if you would post the whole source code.

From what you have posted I assume you don't have something like

using namespace std;

So cos() and sin() are the old C versions. They both expect an input of type double. cosf() and sinf() are the function expecting a float. Since you are using c++ you want to use the STL functions and not the old C ones.

So do the following

static vector2 polar(float r, float phi) {
    return {r * std::cos(phi), r * std::sin(phi)};
}

The STL functions are overloads so they take float and double as input, without any implicit conversions.

Upvotes: 2

Related Questions