我正在尝试我在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());
}
}
一旦我意识到存在compound assignment operator + =的隐式演员,那么“打印什么”这个问题的答案就显而易见了。
但它打印的内容如下:420000006或420000007, 而不是(预期的)420000000(或删除尾随零后的“42”)。
因此,显示从double转换为int并不总是只删除double中的小数部分,如下所示:How to cast a double to an int in Java?
所以我做了一些试验,这是我发现的一个例子:
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 !!!
...所以现在我正在寻找这种行为的解释???
答案 0 :(得分:9)
许多十进制值不可能是双值。在使用它们之前,首先必须将它们挤压到最接近的双值。
示例:16384.999999999999
没有精确的双重表示。最接近的两个值为16384.99999999999636202119290828704833984375
和16385.0
。压缩导致差异大约0.000000000003
,而挤压导致差异0.000000000001
- 上升导致更接近的值,这就是它被解释为。
答案 1 :(得分:8)
也许你会对
这个事实感到惊讶System.out.println(131072.99999999999); // -> 131073 !!!
你没有把事件转换成int。
Java(以及其他语言)中的双重表示存在问题。系统不像人类那样使用“十进制”部分。
这里详细说明: http://en.wikipedia.org/wiki/Floating_point
但是,简而言之,双重值存储的几个部分是为了获得最终结果(图像类似于-1.23 * 10 ^ -15)。而且每个给定的数字只有有限的空间。所以最后你不能完全代表Double.MAX_VALUE和Double.MIN_VALUE之间的每个数字。
答案 2 :(得分:6)
您可以根据需要编写带有任意数字的文字double
,但这并不意味着double
值可以代表您所写的文字。
摆脱int
强制转换,以便更详细地了解文字的double
表示形式在投放到int
之前的含义:
System.out.println(16383.999999999999);
System.out.println(16383.999868686868686999999999);
System.out.println(16384.999999999999);
System.out.println(16385.999999999999);
输出:
16383.999999999998
16383.99986868687
16385.0
16386.0
将这些转换为int,你会看到:
16383
16383
16385
16386
答案 3 :(得分:1)
有两种解释:
你遇到了Round Off Error,其中浮点运算不能像你想要的那样精确,所以它做得最好,但有时会产生奇怪的结果。这就是@ Toomai的答案所暗示的,并由@Ziyao Wei的答案证实
还有Tie-breaking,数字数学有时会以“错误的方式”围绕以保持公平。这与@Lee Harrison的回答相对应,但显然更适用于C而不是Java。
答案 4 :(得分:1)
当你有一个需要多一位的数字时,它意味着一个较少的分数位可用,它必须被舍入。
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));
}
打印
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
答案 5 :(得分:0)
没有错。双值表示实数,实数的数学告诉我们0,(9)= 1 http://www.math.hmc.edu/funfacts/ffiles/10012.5.shtml
由于实数的表示,还存在一些问题,因此具有: 131070.99999999999表示为131070 + 0.99999999999 131072.99999999999表示为131072 + 0.(9)
答案 6 :(得分:-1)
在this site上,详细解释了该行为
答案 7 :(得分:-1)
我认为将一个双精度值转换成整数会导致它们被'银行家四舍五入'舍入。
答案 8 :(得分:-1)
现在我相信这是一个错误。
在Java Spec(5.1.3)上,据说它使用“向零舍入”。
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).
向零舍入(或截断,或远离无穷大)只是(引用维基):
q = truncate(y) = sgn(y) * floor(abs(y)) = -sgn(y) * ceiling(-(abs(y))
q是y的整数部分,没有小数位数。