Reputation: 12770
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
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
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
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
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
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
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
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
Reputation: 2453
I believe that casting a double as an integer causes them to be rounded with 'bankers rounding'.
Upvotes: -1