Странное сужающееся преобразование двойного числа в плавающее предупреждение в компиляторе g ++ - PullRequest
1 голос
/ 07 февраля 2020

У меня есть эта функция для создания вектора из полярной нотации, использующего в нем фигурный инициализатор:

// 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)};
}

В MSVS все выглядит нормально, но компилятор g ++ показывает предупреждения, которые для меня кажутся довольно странными:

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)};
                     ~~^~~~~~~~~~

Если я использую конструктор, предупреждение исчезает:

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

Почему появляется это предупреждение? Означает ли это, что скомпилированная программа выполнит ненужное преобразование из float в double и обратно в float?

ОБНОВЛЕНИЕ Вот минимальный воспроизводимый пример

#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;
}

Вывод 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};
                     ~~~~~~^~

Так что использование std::cos помогает. Но главный вопрос остается (и меня беспокоит) - почему предупреждение появляется только при использовании брекет-инициализации?

Ответы [ 2 ]

2 голосов
/ 07 февраля 2020

Было бы полезно, если бы вы опубликовали весь исходный код.

Из того, что вы опубликовали, я предполагаю, что у вас нет чего-то вроде

using namespace std;

Итак cos() и sin() - это старые C версии. Они оба ожидают ввода типа double. cosf() и sinf() - функция, ожидающая float. Поскольку вы используете c ++, вы хотите использовать функции STL, а не старые C.

Поэтому выполните следующие действия:

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

Функции STL являются перегрузками, поэтому они принимают float и удваивается как ввод без каких-либо неявных преобразований.

1 голос
/ 07 февраля 2020

Вы не используете правильные cos и 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)};
}

Использование std::cos и std::sin дает правильный результат, так как они перегружены, как this :

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

Редактировать: Запуск в проводнике компилятора Просто выдайте то же предупреждение, но нажатие кнопки cppinsights дает более правильную ошибку:

/ home / insights / insights. cpp: 17: 20: ошибка: неконстантное выражение не может быть сужено от типа 'double' до 'float' в списке инициализаторов [-Wc ++ 11-сужение ] return {a * 2, cos (a) * 3}; ^ ~~~~~~~~~ /home/insights/insights.cpp:17:20: примечание: вставьте явное приведение, чтобы заглушить эту проблему return {a * 2, cos (a) * 3}; ^ ~~~~~~~~~

Что затем приводит к определению списка инициализаторов , в котором упоминается list-initialize и подраздел "сужение" "

Инициализация списка сужающихся преобразований ограничивает допустимые неявные преобразования, запрещая следующее:

  • преобразование из типа с плавающей точкой в ​​целочисленный тип
  • преобразование из длинного двойного в двойное или в плавающее и преобразование из двойного в плавающее, за исключением случаев, когда источником является константное выражение и переполнения не происходит
  • преобразование из целочисленного типа в число с плавающей запятой тип, за исключением случаев, когда источником является константное выражение, значение которого может быть точно сохранено в целевом типе
  • преобразование из целочисленного или перечислимого типа с незаданной областью в целочисленный тип, который не может представлять все значения оригинала, кроме случаев, когда источником является константное выражение, значение которого может храниться точно в целевом типе

Так что, если я прочитаю это правильно это должна была быть ошибка.

...