3

Why are the two following result different? The result of the following multiplication should be an integer anyway.

uint16_t test = 500.00 * 128.51;
std::cout << test << std::endl; ----> 64254

but

float test = 500.00 * 128.51;
std::cout << test << std::endl; ----> 64255
vincedjango
  • 1,022
  • 1
  • 13
  • 24
  • 3
    What Every Computer Scientist Should Know About Floating-Point Arithmetic : https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – Mitch Wheat Feb 15 '18 at 01:05
  • This is really basic, any textbook should explain the difference between integer and float. – Barmar Feb 15 '18 at 01:06
  • @MitchWheat It's much more basic than that. He doesn't even understand what an integer is. – Barmar Feb 15 '18 at 01:06
  • Integers are whole numbers, they don't have fractions. – Barmar Feb 15 '18 at 01:07
  • 3
    @Barmar: The OP did not claim that integers have fractions. – Lightness Races in Orbit Feb 15 '18 at 01:07
  • 1
    @Barmar But the exact result of `500.00 * 128.51` is in fact an integer. – Baum mit Augen Feb 15 '18 at 01:08
  • Oh, now I see what he's asking about. – Barmar Feb 15 '18 at 01:08
  • 3
    To complete the linked duplicate, 500.00 * 128.51 is actually slightly smaller than 64255 (as 128.51 cannot be accurately represented in binary FP). When casting to an integral type, decimals are discarded, so your 64254.99999... becomes 64254. OTOH, when keeping it as a double, you preserve the 64254.9999..., and the default cout behavior is to *round* at some decimal position when printing, so when displaying it you see it just as 64255. – Matteo Italia Feb 15 '18 at 01:13
  • http://coliru.stacked-crooked.com/a/03ba7c2125ecbdd7 there it is – Lightness Races in Orbit Feb 15 '18 at 01:13
  • 1
    @MatteoItalia: I've actually re-opened as the general purpose dupe doesn't seem all that useful to be quite honest, particularly with such a subtle cause (intermediate error carried over to an otherwise would-be exact result). I think your comment would be great as an answer here. – Lightness Races in Orbit Feb 15 '18 at 01:14
  • @LightnessRacesinOrbit: I was dubious as well about the dupe, it can be useful as an intro to "why FP is inexact", but single cases are probably best handled on their own, especially if, as in this case, everything gets mixed up with truncation and rounding behavior of the stream. I'll move that comment to an answer. – Matteo Italia Feb 15 '18 at 01:19
  • @MatteoItalia: Yep nice one – Lightness Races in Orbit Feb 15 '18 at 01:20

2 Answers2

6

(moving from a comment)

To complete the previously linked duplicate, 500.00 * 128.51 is actually slightly smaller than 64255 (as 128.51 cannot be accurately represented in binary FP).

When casting to an integral type, decimals are discarded (the value is truncated), so your 64254.99999... becomes 64254.

OTOH, when keeping it as a double, you preserve the 64254.9999..., and the default ostream::operator<< behavior is to round at some decimal position when printing, so when displaying it you see it just as 64255.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
3

The result of the following multiplication doesn't have decimals.

Maybe it doesn't in base 10 (decimal), but computers don't work in base 10. They work in base 2, and in base 2 128.51 has more decimals fractional digits than can be stored in the float (or, actually, any similar format — it would require infinite places, much like trying to write out 1/3 in decimal).

This error carries through so, although mathematically 64255 is a whole number, the result of your multiplication is closer to 64254.9999999999927240.

std::cout is automatically rounding this up by default (because it knows that this kind of thing happens); however, during conversion to uint16_t, a dumb truncation happens instead, resulting in the incorrect 64254.

With some I/O manipulator magic, we can see this more clearly:

#include <iostream>
#include <iomanip>

int main()
{
    std::cout << std::fixed << std::setprecision(16) << (500.00 * 128.51) << std::endl;
    std::cout << uint16_t(500.00 * 128.51) << std::endl;
}

// 64254.9999999999927240
// 64254

(live demo)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055