2

As I understand this, some numbers can't be represented with exactitude in binary, and that's why floating-point arithmetic sometimes gives us unexpected results; like 4.35 * 100 = 434.99999999999994. Something similar to what happens with 1/3 in decimal.

That makes sense, but this induces another question. Seems that in binary both 4.35 and 435 can be represented with exactitude. That's when it stops making sense to me. Why does 4.35 * 100 evaluates to 434.99999999999994? 435 and 4.35 have an exact representation in the double type dynamics:

double number1 = 4.35;
double number2 = 435;
double number3 = 100;
System.out.println(number1); // 4.35
System.out.println(number2); // 435.0
System.out.println(number3); // 100.0
// So far so good. Everything ok. 

System.out.println(number1 * number3); // 434.99999999999994 !!!
// But 4.35 * 100 evaluates to 434.99999999999994

Why?

Edit: this question was marked as duplicate, and it is not. As you can see in the accepted answer, my confusion was regarding the discrepancy between the actual value and the printed value.

NPN328
  • 1,863
  • 3
  • 27
  • 47
  • 2
    see: http://stackoverflow.com/a/11737784/1426079 – Brinnis Aug 22 '13 at 19:43
  • 2
    The closest `double` value to `4.35` is `4.3499999999999996447286321199499070644378662109375`. Multiplying by 100 produces `434.99999999999994315658113919198513031005859375`. – Daniel Fischer Aug 22 '13 at 19:49
  • @PascalCuoq Both are different question. See accepted answer for details. Sorry if I was not clear enough. – NPN328 Aug 22 '13 at 19:55
  • @JCPedroza No problem, just thought you'd like to know that before actually looking at it, it was *a priori* quite likely that `4.35 * 100` would have computed as `435`. By the way, fractional floating-point numbers have either .5 or a sequence of digits that end in 25 or in 75 as fractional part. – Pascal Cuoq Aug 22 '13 at 20:01
  • 1
    @JCPedroza Once you decide to look at it, you see that 435 is just below 512, relatively much, much closer to 512 than 4.35 is to 8, which means that there was a risk `4.35 * 100` would not compute as 435. – Pascal Cuoq Aug 22 '13 at 20:05

4 Answers4

12

Seems that in binary both 4.35 and 435 can be represented with exactitude.

I see that you understand how the floating point numbers are internally represented. As for your doubt, no 4.35 does not have an exact binary representation. So the issue is, why the 1st print statement prints 4.35.

That is happening because System.out.println() invokes the Double.toString(double) method, which in turns uses FloatingDecimal#toJavaFormatString() method, which performs some rounding internally on the passed double argument. You can go through the source code I linked.

For seeing the actual value of 4.35, try using this:

BigDecimal bd = new BigDecimal(number1);
System.out.println(bd);

This will print:

4.3499999999999996447286321199499070644378662109375

In this case, rather than printing the double value, you create a BigDecimal object passing double value as argument. BigDecimal represents arbitrary precision signed decimal number. So it gives you the exact value of 4.35.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
8

You are right in that sometimes floating-point arithmetic gives unexpected results.

Your assertion that 4.35 can be represented exactly in floating-point is incorrect, because it can't be represented as a terminating binary decimal. 100 can obviously be represented exactly, so for the result to be 434.99999999999994, `4.35 must not be represented exactly.

To be represented exactly in floating-point, a number must be able to be converted to a fraction where the denominator is a power of two only (and it must not be so precise that it exceeds the maximum precision of the floating-point type you're using). In this case, 4.35 is 4 7/20, and the denominator has a factor of 5, so the number can't be represented exactly in binary.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • What is happening when I print 4.35 directly and it evaluates to 4.35 instead of 434.99999999999994? If 4.35 doesn't have an exact representation in binary, shouldn't 4.35 evaluate to 434.99999999999994? That's the source of my confusion. – NPN328 Aug 22 '13 at 19:49
  • 3
    When Java converts a number such as `4.3499999999999996447286321199499070644378662109375` to a `String`, it will round it to `4.35`, but that rounding point exposes the inexact representation for `434.99999999999994`, because it looks like the rounding point is past that last digit for the latter but not the former. – rgettman Aug 22 '13 at 19:55
1

Although from a hardware perspective each floating-point number represents some exact value of the form M * 2^E (where M and E are integers in a certain range), from a software perspective it is more helpful to think of each floating-point number as representing "Something for which M * 2^E has been deemed the best representation, and which is hopefully close to that". Given a floating-point value (M * 2^E), one should figure that the actual number it's intended to represent may very easily be anywhere from (N - 1/2) * 2^E to (N + 1/2) * 2^E and in practice may extend a bit further beyond.

As a simple example, with type float, the value of M is limited to the range 0-16777215. The best representation of 2000000.1f is thus 16000001 * 2^-3 [i.e. 16000001/8]. Although exact decimal value of 16000001/8 is 2000000.125, the last digit isn't necessary to define the value of the number, since 16000001/8 would the best representation of 2000000.120 and 2000000.129 (or, for that matter, all values between 2000000.0625 and 2000000.1875, non-inclusive). Because the number of digits that would required to display the exact decimal value of a number of the form M * 2^E would often far exceed the number of meaningful digits, it is common to limit number of displayed digits to roughly those necessary to uniquely define the value.

Note that if one regards floating-point numbers as representing ranges, one will observe that casts from double to float--even though they must be explicitly specified--are actually safe since converting the double that best represents a particular value to float will yield either the best float representation of that value or something very close to it. Conversely, conversion from float to double, even though it's allowed implicitly, is dangerous because such conversion is very unlikely to select the double which would best represent the number that the float was supposed to represent.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

it is a bit hard to explain in English, because I have learned computer number representation in Hungarian. In short, 4.35, 435 nor 100 is not exactly these numbers, but mantissa * 2^k (k-characteristic from -k to +k, and t - is the length of the mantissa in the M = (t,-k,+k) ) although the print call does some rounding. So the number-line is not continuous, but near some famous points, denser ).

floating point number line

So as I think these numbers are not exactly what you expect, and after the operation (I suppose this is one or two simple binary operation) you get the multiple of error distance of the two float point number representation.

csikos.balint
  • 1,107
  • 2
  • 10
  • 25