从double转换为int并不总是只丢弃小数部分

时间:2011-12-06 16:18:52

标签: java

我正在尝试我在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 !!!

...所以现在我正在寻找这种行为的解释???

9 个答案:

答案 0 :(得分:9)

许多十进制值不可能是双值。在使用它们之前,首先必须将它们挤压到最接近的双值。

示例:16384.999999999999没有精确的双重表示。最接近的两个值为16384.9999999999963620211929082870483398437516385.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的整数部分,没有小数位数。