Search code examples
c++floating-pointg++doublenarrowing

Why doesn't braced initialization throw a narrowing error when converting from double to float?


Two things every C++ tutorial mentions early on:

  1. Braced initialization is generally superior when possible, because it will throw an error during a narrowing conversion such as
int narrow{1.7}; // error: narrowing conversion of '1.7e+0' from 'double' to 'int'
  1. You must explicitly declare floats as float literals, otherwise they will default to double literals
float some_float{1.7f};

However, when working with the g++ compiler on Windows, I've discovered something odd -

float narrow{1.7}; // still a float despite no f postfix
double not_narrow{1.7}; // actually a double

This code compiles without any errors, and sizeof(narrow) returns 4, where sizeof(not_narrow) returns 8, as expected.
Hovering over 1.7, VSCode identifies it as a double literal ~=1.699999999999999956, but float has then narrowed it to ~=1.700000048.
I thought it might be simply that 1.7 is both valid as a float and a double, so I tried

float narrow{1.699999999999999956};

but this produces an identical result.
Why doesn't braced initialization throw an error (warning, diagnostic message, anything) here for narrowing a double literal to a float? Is this g++ specific, or a quirk of C++ in general? I'd love to understand better.


Solution

  • A conversion from a floating-point type to a shorter floating-point type is not a narrowing conversion if "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)" (C++20 [dcl.init.list]/7.2).

    If you think about it, double{1.7} and float{1.7} are most likely both inexact. But if you write the latter, it's reasonable to assume that you mean it, and there's not much to be gained from prohibiting this.