MSVC 用双打支撑初始化似乎违反了标准?

MSVC brace initialization with doubles appears to violate the standard?

看看这个简单的程序:

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
}

使用默认选项(cl /W4,版本 19.00.23918)使用 MSVC 2015 编译时,我收到以下消息:

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 测试),仅对未使用的变量发出警告。此外,removing/commenting 行 f4f6 导致编译成功(只有第 f2 行的一个警告)。

最初看起来 MSVC 只是告诉我 7.2 不能精确地表示为 float,所以它是一个收缩转换(这在大括号初始化中是非法的)。但是,标准 (draft N3337) 第 8.5.4 节注释 7 是这样说的:

A narrowing conversion is an implicit conversion...

  • from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly)

强调我的。由于7.2在float可表示的取值范围内,所以其转换为float按照标准不应该是窄化转换。 MSVC 在这里出错了吗,我应该提交错误吗?

它确实看起来像一个错误。作为解决方法,以下内容似乎可以消除 MSVC 2015 中的错误和警告。

#pragma float_control(precise, off, push)

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

#pragma float_control(precise, pop)

如果使用禁用 MS 语言扩展的 /fp:fast compiler switch, though that one is incompatible with /Za,同样适用于全球。

有些浮点数可以用 float 表示法精确表示,有些则不能。如果数字可以用 x / 2^y 的形式表示,其中 x 是任何整数,而 y 是等于或小于 23 的整数,则它适合。 大多数 十进制数不能用这种方式表示,因为二进制数会永远重复。 7.2 是一个例子。

您可以通过将 f 附加到每个数字来轻松解决此问题,以向编译器指示这是一个 float 常量而不是 double.

float f4{ 7.2f };