52

I have the following code:

Double i=17.31;
long j=(long) (i*100);
System.out.println(j);

O/P : 1730 //Expected:1731

Double i=17.33;
long j=(long) (i*100);
System.out.println(j);

O/P : 1732 //Expected:1733

Double i=17.32;
long j=(long) (i*100);
System.out.println(j);

O/P : 1732 //Expected:1732{As expected}

Double i=15.33;
long j=(long) (i*100);
System.out.println(j);

O/P : 1533 //Expected:1533{as Expected}

I have tried to Google but unable to find reason.I am sorry if the question is trivial.

PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142
xyz
  • 2,160
  • 3
  • 20
  • 31
  • 3
    Not sure, hence the comment but this could be due to rounding issues. If that is the case, [this](http://stackoverflow.com/questions/960072/rounding-errors) previous SO thread should be of help. The [BigDecimal](http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html) class should help reduce these errors. – npinti Nov 20 '12 at 06:44
  • 3
    Why do people keep massively updated each iteration of "Q: 'Floating point weirdness' A: 'IEEE-754'" instead of flagging/voting to close as exact duplicate? – Dan Is Fiddling By Firelight Nov 20 '12 at 17:53
  • As a matter of habit, if you want to convert a decimal number to an int and the function you want to use truncates, add .5 before truncating. It will give you a better rounding (1.75 will round to 2 instead of 1) but most importantly will avoid this problem. – Bill K Nov 20 '12 at 19:15
  • 3
    @DanNeely I looked for a duplicate, but couldn't find one addressing the rounding issue exactly. Everyone links to [**this damn doc**](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) which is long enought to be book in itself. :) – Anirudh Ramanathan Nov 20 '12 at 22:42

7 Answers7

78

None of the answers seem to deal with why 17.32 acted different.

1. Why it occurred

The difference in behaviour you see between 17.32 and 17.33 & 17.31 is due to IEEE-754 Rounding rules.

Rounding rule applied: from, The Java™ Virtual Machine Specification §2.8.1

The rounding operations of the Java virtual machine always use IEEE 754 round to nearest mode. Inexact results are rounded to the nearest representable value, with ties going to the value with a zero least-significant bit. This is the IEEE 754 default mode. The Java virtual machine does not give any means to change the floating-point rounding mode


2. Your case:

Double is: (1 sign-bit + 11 exponent-bits + 52 fraction-bits = 64bits). Internal representation after rounding below:

             1 [63]      11 [62-52]           52 [51-00]
              Sign        Exponent             Fraction

17.31 -->    0 (+)       10000000011 (+4)     1.0001010011110101110000101000111101011100001010001111
17.32 -->    0 (+)       10000000011 (+4)     1.0001010100011110101110000101000111101011100001010010 //rounded up
17.33 -->    0 (+)       10000000011 (+4)     1.0001010101000111101011100001010001111010111000010100

3. Internal representation (Proof):

17.31: (Mantissa comparison)

Actual:   1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111

17.32: (Mantissa comparison)

Actual:   1.00010101000111101011100001010001111010111000010100011... 
Internal: 1.0001010100011110101110000101000111101011100001010010    //round-up!

17.33: (Mantissa comparison)

Actual:   1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100

4. Conversion back-to-decimal:

17.31 ->  17.309999999999998721023075631819665431976318359375...
17.32 ->  17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 ->  17.3299999999999982946974341757595539093017578125...

(IEEE-754 Analysis Tool)

5. Cast to long

EDIT: There is a factor more at play at your multiplication step as @Jeppe Stig Nielsen said. The result of the FP multiplication (Reference) step does its own rounding-towards-nearest. This changes which results are as expected and which aren't, but the reason is still exactly the same as stated above.

Finally, due to the cast (long), truncation occurs, and leaves you with the results you see. (1730, 1732, 1732)

Narrowing Primitive Conversion : The Java™ Language Specification §5.1.3

If the floating-point number is not an infinity, the floating-point value is rounded to an integer value V, rounding toward zero using IEEE 754 round-toward-zero mode

Anirudh Ramanathan
  • 46,179
  • 22
  • 132
  • 191
  • 4
    Upvoted this answer also because of the link to the nice tool. But: Remember that the multiplication by `100.0` leads to further rounding. Consider the case **`17.34`**. The representation of this number as a `Double` _also_ has to be rounded _down_. But it still gives the expected output after multiplying by `100.0`! The reason is that part of the multiplication algorithm is to round to get rid of digits that can't fit into the result `Double`. In the case of `17.34 * 100.0` the rounding _after_ multiplying is a round-_up_, and therefore we're lucky to get the "expected" result. – Jeppe Stig Nielsen Nov 20 '12 at 11:08
22

The double value is represented not as 17.31, but as 17.309999999999999. That's why when you multiply it by 100 you get 1730.99999999999999999. After conversion to Long your double value is truncated towards zero. So you get 1730.

svz
  • 4,516
  • 11
  • 40
  • 66
6

Cthulhu and svz's answers are correct. If you want to multiply doubles by 100 and avoid floating point rounding errors, you can use Math.round() to round the result to the closest long after each multiplication:

Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);

This will still have floating point error when dealing extremely large (or negative) doubles. The larger the absolute value of a double, the more the difference is between it and the next double that Java can represent. After some point, consecutive doubles are more than an integer apart, and conventional rounding won't be able to smooth out the difference. For the examples you posted, this should work, though.

Kevin
  • 14,655
  • 24
  • 74
  • 124
5

As has been explained, this is due to very small floating point precision.

This can be resolve via using a Math.round(), command, as follows:

long j=Math.round(i*100);

This will allow the program to compensate for the very small errors which are inherit using floating point calculations, by not using a floor operation, as the default (long) does.

PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142
4

It has to do with the internal representation. If you take a look at i*100 in the first case, you'll see that it is 1730.9999999999998. The cast will only remove the part after the point (truncated).

Burkhard
  • 14,596
  • 22
  • 87
  • 108
2

For value like, String value = 1074.6

Use below code

double amount = Double.parseDouble(value);

String roundedValue = String.valueOf(Math.round(amount * 100))

You can get result 107460

Dhruv Patel
  • 519
  • 4
  • 5
0

When you do this kind of long conversion it's floor. Your 17.31 could actually be 17.30999999999 and that's why it resulted in 1730 instead of 1731.

use i = i * 100, then i.longValue() will solve the problem.

Gang Su
  • 1,187
  • 10
  • 12
  • 1
    `i.longValue` acts exactly the same way as `(long) i`. http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Double.html#longValue() – John Dvorak Nov 20 '12 at 06:50