2

The following code:

    float numberForFactorial;
    float floatingPart = 1.400000;
    int integralPart = 1;

    numberForFactorial = ((floatingPart) - (float)integralPart) * 10;

    printf("%d", (int)numberForFactorial);

Returns 3 instead of 4. Can you explain me why?

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Nikitas
  • 1,013
  • 13
  • 27
  • Probably because `1.40000` gets stored internally as `1.399999` or something like that. `(int)3.99999` is `3`, as expected. – Mad Physicist Aug 30 '16 at 18:57
  • What do you get when you do `printf("%0.6f\n", floatingPart);`? – Mad Physicist Aug 30 '16 at 18:58
  • To expand on @MadPhysicist's comment, conversion from floating-point to integer truncates toward zero. – Keith Thompson Aug 30 '16 at 18:58
  • Please see [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Weather Vane Aug 30 '16 at 19:00
  • 1
    yes i already know that the rounding of the floating point is one of the most tricky parts. (even people had died) but there is nothing in ANSI C that i can do to get the integer with value 3? – Nikitas Aug 30 '16 at 19:01
  • 1
    Yes, add `0.5` to the computation (if the number is positive), or use `round` . – Weather Vane Aug 30 '16 at 19:02
  • i added 0.00001 before i make this question and it worked! but i thought that this is just a 'hack' that came up through my mind.... – Nikitas Aug 30 '16 at 19:05
  • @Mad Physicist: I tried your printf and returned: `4.000000` – Nikitas Aug 30 '16 at 19:06
  • That is because `printf` rounds the digit you don't see, in this case the 7th decimal place. – Weather Vane Aug 30 '16 at 19:07
  • You are relying on `floatingPart` being represented exactly and writing code that will fail if the representation is even the tiniest bit less than precisely 1.4 -- don't do that. – David Schwartz Aug 30 '16 at 19:11
  • @Nikitas. My answer has a link to ideone example. You are correct that `0.6f` does not print the desired result. I had to use `0.8f` or more to start seeing the discrepancy. – Mad Physicist Aug 30 '16 at 19:12

2 Answers2

2

This is due to binary representation of floating-point values. More specifically, the 0.4 or 2/5 cannot be expressed with mantissa as sum of any combination like 1/2 + 1/4 + 1/8 + ...

The literal 1.400000 is stored as something closer to 1.399999976158142 in its binary representation. The cast to int truncates non-integer part, giving three as the final result.

To be pedantic, the C standard does not require binary-based representation of floating-point data type, however IEEE 754 is de facto the standad one in today's computing.

Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • Hm... yes i know about the 1/2 + 1/4 + 1/8 thing when i studied "Computer Systems A Programmer's Perspective" Third Edition, Chapter 2, page 110 – Nikitas Aug 30 '16 at 19:11
2

The float closest to 1.400000 is slightly less than 1.4. You can verify that by doing

printf("%0.8hf\n", floatingPart);

The result from ideone is 1.39999998. This means that 10 times the first digit after the decimal point is 3.

To avoid this issue, use rounding instead of truncation. One easy way to round is by adding half before truncation:

printf("%d", (int)(numberForFactorial + 0.5f));

will print 4 as you were expecting. You can also use round, rint, lround, or modf to get the same result: https://www.gnu.org/software/libc/manual/html_node/Rounding-Functions.html. Rounding is a complex topic, so choose the method whose constraints match your situation best.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • @Nikitas. Feel free to select this answer if it explained what needed explaining. – Mad Physicist Aug 30 '16 at 19:17
  • And yeah, floats are a pain in the a**. – Mad Physicist Aug 30 '16 at 19:17
  • Of course i would do but i just wonder if "adding half before truncation" - which works perfectly - is a well known, accepted technique. On the other hand cannot find any other solution that do the job. – Nikitas Aug 30 '16 at 19:21
  • well i stopped thinking and googled a while and yes. accepted. thanks – Nikitas Aug 30 '16 at 19:23
  • 1
    It is a very common technique: https://www.google.com/#q=rounding+by+adding+0.5. The key is that `0.5` has an exact binary representation. You can't mess up with it. Well, you can, but it would be much harder. – Mad Physicist Aug 30 '16 at 19:23
  • Rounding via `(int)(numberForFactorial + 0.5f)` fails for a number of cases. Better to use `round()` or `rint()`. – chux - Reinstate Monica Aug 30 '16 at 19:59
  • 1
    @chux. Can you give an example? – Mad Physicist Aug 30 '16 at 20:11
  • Candidates include 1) Any `-odd.5` 2) Any value not near the `[INT_MIN...INT_MAX]` range 3) Values where `x + 0.5` forms an inexact sum but would form an exact sum had the floating point number 1 or 2 more bits of precision. 4) The value just smaller than 0.5 5) See [Harder than it looks: rounding float to nearest integer, part 1](http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1) – chux - Reinstate Monica Aug 30 '16 at 20:32
  • [@Shafik Yaghmour](http://stackoverflow.com/a/24348037/2410359) has a good SO post (even though it is C++) it relates to C. – chux - Reinstate Monica Aug 30 '16 at 20:50
  • @chux Fair enough. I included a nod to your comment in the answer. Since the question is not really about that, I think that should be enough. – Mad Physicist Aug 30 '16 at 20:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122237/discussion-between-chux-and-mad-physicist). – chux - Reinstate Monica Aug 30 '16 at 21:13