0

In this underlying code:

#include<iostream>
#include<bitset>
#include<climits>

int main()
{
    float j=9.03445;   //Even double yields same result
    int i={j};
    std::cout <<sizeof(float)<<std::endl;
    return 0;
}

Why is narrowing conversion taking place since both float and int are of 4 bytes? There shouldn't be any loss of bits.

And why this works fine:

float j=9.03445;   
int i(j);
Shreevardhan
  • 12,233
  • 3
  • 36
  • 50
  • 4
    Information is being lost; converting in the other direction won't yield the original value. That's the rough description of "narrowing" (the formal definition in [**\[dcl.init.list\]/7**](http://eel.is/c++draft/dcl.init.list#7) lists all the cases exhaustively, of which "from a floating-point type to an integer type" is one). – Igor Tandetnik Nov 12 '17 at 17:51
  • 1
    This accepted SO questions answer [warning-narrowing-conversion-c11](https://stackoverflow.com/questions/27844971/warning-narrowing-conversion-c11) might help. `{}` catches narrowing conversions, `()` does not – Patrick Artner Nov 12 '17 at 17:52
  • `int` cannot store floating point numbers, so you'd lose the 0.03445 part, if you have written `int i = j;`. – geza Nov 12 '17 at 17:55
  • @geza then how 2nd initialization (direct) is working fine? –  Nov 12 '17 at 17:56
  • 1
    `{}` doesn't allow narrowing (that's one of its major feature). `()` does. Please read the comment above, Patrick already told this. (So it means that in the second case, `i` contains 9, instead of 9.03445, you can easily verify this). – geza Nov 12 '17 at 17:56
  • @geza so it is only not allowing the truncation which other wise works fine with direct initialization or assignment? –  Nov 12 '17 at 18:00
  • 1
    @infinite: exactly. – geza Nov 12 '17 at 18:01
  • @IgorTandetnik In this article http://eel.is/c++draft/dcl.init.list#7 under section 7.2 it says that a constant expression with suitable range, this conversion is allowed in case of list initialization {}, but this code fails with that:: const float i=2.5; int j{i}; **but** this one works fine: const int i=122; char c{i}; **which was earlier throwing error** –  Nov 12 '17 at 18:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/158810/discussion-between-infinite-and-geza). –  Nov 12 '17 at 18:43
  • (7.2) applies to conversions, and I quote, "from `long double` to `double` or `float`, or from `double` to `float`". The conversion in your code is covered by bullet (7.1), which has no such relaxation. (7.2) allows losing precision when moving from more precise to less precise type - otherwise even something as seemingly benign as `float f{1.1};` would produce errors (because `1.1` is of type double, and it's not exactly representable in binary so it's actually something like `1.10....01`; in other words, `float(1.1) != double(1.1)` ) – Igor Tandetnik Nov 12 '17 at 18:54

2 Answers2

1

An int cannot store non-integer numbers. So, there is narrowing at the line int i{j}; (no matter, whether sizeof(int)==sizeof(float). These two types store different kind of numbers).

The {} syntax doesn't allow narrowing, while () syntax does, that's why your second example compiles. In that sample, i will contain 9 (instead of 9.03445), hence the narrowing.

geza
  • 28,403
  • 6
  • 61
  • 135
1

The word "narrowing" is a bit of a misnomer. int i = 1; float f = {i}; is narrowing, but so is float f = 1.0f; int i = {f}; !

Here is the exact definition of narrowing conversion from the C++14 standard:

A narrowing conversion is an implicit conversion

  • from a floating-point type to an integer type, or
  • 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), or
  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.

I approach this by just thinking of it as an arbitrary technical term, not as any actual statement about the number of bits in the value or whatever.

In your question, int i(j); works fine because narrowing conversion is permitted when using parentheses, but not when using curly braces.

M.M
  • 138,810
  • 21
  • 208
  • 365