pgras
pgras

Reputation: 12770

cast from double to int is not always just dropping decimal part

I'm experimenting with the code I found here The Java Specialists' Newsletter.

public class MeaningOfLife {
  public static String findOutWhatLifeIsAllAbout() {
  int meaning = 0;
  for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 20; j++) {
      for (int k = 0; k < 300; k++) {
        for (int m = 0; m < 7000; m++) {
          meaning += Math.random() + 1;
        }
      }
    }
  }
  return String.valueOf(meaning).replaceAll("0*$", "");
  }

public static void main(String[] args) {
  System.out.println(findOutWhatLifeIsAllAbout());
}
}

The answer to the question "what does it print" seemed obvious once I realized that there is an implicit cast with the compound assignment operator +=.

But it printed something like: 420000006 or 420000007, instead of (the expected) 420000000 (or "42", after removing trailing zeros).

So that was showing that casting from double to int is not always just dropping the decimal part of the double as stated here: How to cast a double to an int in Java?

So I made some trials and here is an example of what I found:

System.out.println((int)  (131070.99999999999)); // -> 131070
System.out.println((int)  (131071.99999999999)); // -> 131071
System.out.println((int)  (131072.99999999999)); // -> 131073 !!!
System.out.println((int)  (131073.99999999999)); // -> 131074 !!!


System.out.println((int)  (16382.999999999999)); // -> 16382
System.out.println((int)  (16383.999999999999)); // -> 16383
System.out.println((int)  (16384.999999999999)); // -> 16385 !!!
System.out.println((int)  (16385.999999999999)); // -> 16386 !!!

...So now I'm looking for an explanation for that behavior ???

Upvotes: 11

Views: 3432

Answers (9)

AlexandruB
AlexandruB

Reputation: 1

There is nothing wrong. Double value represent real numbers and the math of real numbers tells us that 0,(9) = 1 http://www.math.hmc.edu/funfacts/ffiles/10012.5.shtml

Also there are some issues due to the representation of real numbers thus having: 131070.99999999999 represented as 131070 + 0.99999999999 131072.99999999999 represented as 131072 + 0.(9)

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533492

When you have a number which requires one more bit, it means one less fraction bit is available and it has to be rounded.

for(int i=4;i<=128*1024;i*=2) {
    double smallestFraction = Math.ulp((double) i-2);
    System.out.println(new BigDecimal(i) + " minus "+new BigDecimal(smallestFraction)+" is "+new BigDecimal(i-smallestFraction));
    System.out.println(new BigDecimal(i+1) + " minus "+new BigDecimal(smallestFraction)+" is "+new BigDecimal(i+1-smallestFraction));
}

prints

4 minus 4.44089209850062616169452667236328125E-16 is 3.999999999999999555910790149937383830547332763671875
5 minus 4.44089209850062616169452667236328125E-16 is 5
8 minus 8.8817841970012523233890533447265625E-16 is 7.99999999999999911182158029987476766109466552734375
9 minus 8.8817841970012523233890533447265625E-16 is 9
16 minus 1.7763568394002504646778106689453125E-15 is 15.9999999999999982236431605997495353221893310546875
17 minus 1.7763568394002504646778106689453125E-15 is 17
32 minus 3.552713678800500929355621337890625E-15 is 31.999999999999996447286321199499070644378662109375
33 minus 3.552713678800500929355621337890625E-15 is 33
64 minus 7.10542735760100185871124267578125E-15 is 63.99999999999999289457264239899814128875732421875
65 minus 7.10542735760100185871124267578125E-15 is 65
128 minus 1.42108547152020037174224853515625E-14 is 127.9999999999999857891452847979962825775146484375
129 minus 1.42108547152020037174224853515625E-14 is 129
256 minus 2.8421709430404007434844970703125E-14 is 255.999999999999971578290569595992565155029296875
257 minus 2.8421709430404007434844970703125E-14 is 257
512 minus 5.684341886080801486968994140625E-14 is 511.99999999999994315658113919198513031005859375
513 minus 5.684341886080801486968994140625E-14 is 513
1024 minus 1.136868377216160297393798828125E-13 is 1023.9999999999998863131622783839702606201171875
1025 minus 1.136868377216160297393798828125E-13 is 1025
2048 minus 2.27373675443232059478759765625E-13 is 2047.999999999999772626324556767940521240234375
2049 minus 2.27373675443232059478759765625E-13 is 2049
4096 minus 4.5474735088646411895751953125E-13 is 4095.99999999999954525264911353588104248046875
4097 minus 4.5474735088646411895751953125E-13 is 4097
8192 minus 9.094947017729282379150390625E-13 is 8191.9999999999990905052982270717620849609375
8193 minus 9.094947017729282379150390625E-13 is 8193
16384 minus 1.818989403545856475830078125E-12 is 16383.999999999998181010596454143524169921875
16385 minus 1.818989403545856475830078125E-12 is 16385
32768 minus 3.63797880709171295166015625E-12 is 32767.99999999999636202119290828704833984375
32769 minus 3.63797880709171295166015625E-12 is 32769
65536 minus 7.2759576141834259033203125E-12 is 65535.9999999999927240423858165740966796875
65537 minus 7.2759576141834259033203125E-12 is 65537
131072 minus 1.4551915228366851806640625E-11 is 131071.999999999985448084771633148193359375
131073 minus 1.4551915228366851806640625E-11 is 131073

Upvotes: 1

zw324
zw324

Reputation: 27180

Now I believe this is a bug.

On Java Spec (5.1.3), it is said that it uses "rounding towards zero".

Otherwise, 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 (§4.2.3).

And round towards zero (or truncate, or round away from infinity) is just (quoting wiki):

q = truncate(y) = sgn(y) * floor(abs(y)) = -sgn(y) * ceiling(-(abs(y))

q is the integer part of y, without its fraction digits.

Upvotes: -1

Hurda
Hurda

Reputation: 4715

Maybe you would be amazed with the fact that

System.out.println(131072.99999999999); // -> 131073 !!!

You dont have to event cast it into the int.

There is problem with double representation in Java (and other languages too). The system doesn't use 'decimal' part as we human does.

It's in length explained here: http://en.wikipedia.org/wiki/Floating_point

But, in short, the double value is store as few parts that are put togather to get final result (image something like -1.23*10^-15). And you have only limited space for each of these given number. So in the end you can't exactly represent every number beween Double.MAX_VALUE and Double.MIN_VALUE.

Upvotes: 8

Jonathon Faust
Jonathon Faust

Reputation: 12545

You can write a literal double with as many digits as you want, but that doesn't mean a double value can represent the literal you wrote.

Get rid of the int cast to see more closely what the double representation of your literal is going to be before it's casted to int:

System.out.println(16383.999999999999);
System.out.println(16383.999868686868686999999999);
System.out.println(16384.999999999999);
System.out.println(16385.999999999999);

Output:

16383.999999999998
16383.99986868687
16385.0
16386.0

Cast these to int, and you'll see:

16383
16383
16385
16386

Upvotes: 6

Kane
Kane

Reputation: 4157

There are two explanations:

You're running into a Round Off Error, where the floating point arithmetic can't be as precise as you want, so it does it's best, but sometimes produces weird results. This is what @Toomai's answer alludes to, and is confirmed by @Ziyao Wei's answer

There is also Tie-breaking, where digital math will sometimes round "the wrong way" in an attempt to maintain fairness. This corresponding to @Lee Harrison's answer, but apparently is applicable more to C than to Java.

Upvotes: 1

Toomai
Toomai

Reputation: 4214

Many decimal values are not possible double values. Before they can be used they first have to be squeezed to whatever double value is closest.

Example: 16384.999999999999 has no exact double representation. The two closest values are 16384.99999999999636202119290828704833984375 and 16385.0. Squeezing down results in a difference of about 0.000000000003, while squeezing up results in a difference of 0.000000000001 - going up results in a closer value, so that's what it's interpreted as.

Upvotes: 9

Lee Harrison
Lee Harrison

Reputation: 2453

I believe that casting a double as an integer causes them to be rounded with 'bankers rounding'.

Upvotes: -1

Robin
Robin

Reputation: 36601

On this site the behavior is explained in full detail

Upvotes: -1

Related Questions