Инициализация скобок MSVC с удвоением, по-видимому, нарушает стандарт?

Проверьте эту простую программу:

int main() {
    float f2 = 7.2; // OK, with warning
    float f3 = 7.199999809265137; // OK, no warning
    float f4{ 7.2 }; // Fails
    float f5{ 7.199999809265137 }; // OK, no warning
    float f6 = { 7.2 }; // Fails
    float f7 = { 7.199999809265137 }; // OK, no warning
}

При компиляции с MSVC 2015 с использованием параметров по умолчанию (cl /W4, версия 19.00.23918), я получаю следующие сообщения:

FloatTest.cpp(2): warning C4305: 'initializing': truncation from 'double' to 'float'
FloatTest.cpp(4): error C2397: conversion from 'double' to 'float' requires a narrowing conversion
FloatTest.cpp(4): warning C4305: 'initializing': truncation from 'double' to 'float'
FloatTest.cpp(6): error C2397: conversion from 'double' to 'float' requires a narrowing conversion
FloatTest.cpp(6): warning C4305: 'initializing': truncation from 'double' to 'float'

Эта программа отлично компилируется с помощью Clang 3.0-3.8 и GCC 4.5.4-6.1.0 (протестирована с помощью http://melpon.org/wandbox), и только предупреждения для неиспользуемых переменных. Кроме того, удаление/комментирование строк f4 и f6 приводит к успешной компиляции (только с одним предупреждением для строки f2).

Изначально это похоже на то, что MSVC просто говорит мне, что 7.2 не может быть представлен точно как float, поэтому это сужение преобразования (что является незаконным при инициализации скобки). Тем не менее, стандарт (черновик N3337), раздел 8.5.4, примечание 7, говорит следующее:

Сужение преобразования является неявным преобразованием...

  • от long double до double или float, или от double до float, за исключением случаев, когда источник является константным выражением, а фактическое значение после преобразования находится в диапазоне значений, которые могут быть представлены (, даже если он не может быть представлен точно)

Акцент мой. Так как 7.2 находится в диапазоне значений, представляемых float, его преобразование в float не должно быть сужающим преобразованием в соответствии со стандартом. Является ли MSVC неправильным здесь, и должен ли я указать ошибку?

Ответ 1

Похоже на ошибку. Для обходного пути в MSVC 2015, как правило, затухают как ошибки, так и предупреждения.

#pragma float_control(precise, off, push)

float f2 = 7.2; // OK, with warning
//...

#pragma float_control(precise, pop)

То же самое работает глобально, если использовать переключатель /fp:fast, хотя он несовместим с /Za, который отключает расширения языка MS.

Ответ 2

Некоторые числа с плавающей запятой могут быть точно выражены в представлении float, а некоторые не могут. Если число может быть представлено в форме x / 2^y, где x - любое целое число, а y - целое число 23 или меньше, оно подходит. Большинство десятичных чисел не могут быть представлены таким образом, как двоичное число, которое они повторяют навсегда. 7.2 - один из примеров.

Вы можете легко исправить это, добавив f к каждому номеру, чтобы указать компилятору, что это константа float, а не double.

float f4{ 7.2f };