2

假设我们有这种循环(伪代码)

double d = 0.0
for i in 1..10 {
    d = d + 0.1
    print(d)
}

在 C 中,printf("%f", d)我得到了这个:

0.100000
0.200000
0.300000
...
1.000000

在 C++ 中,cout << d我得到了这个:

0.1
0.2
...
1

在Java中,System.out.println(d)我得到了这个:

0.1
0.2
0.3 (in debug mode, I see 0.30000000000004 there but it prints 0.3)
...
0.7
0.799999999999999
0.899999999999999
0.999999999999999

所以我的问题是:

  1. 为什么这个简单的代码在 Java 中打印得如此糟糕并且在 C 中是正确的?
  2. 这在其他语言中表现如何?
4

3 回答 3

7

正如这里所回答的,这与任何语言都无关。

见这里:每个程序员都应该知道的关于浮点运算的知识

实数是无限的。计算机使用有限数量的位(今天的 32 位,64 位)。因此,计算机完成的浮点运算不能代表所有实数。0.1 是这些数字之一。

请注意,这不是与 Ruby 相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。

于 2012-01-17T14:33:12.033 回答
6

为什么这个简单的代码在 Java 中打印得如此糟糕并且在 C 中是正确的?

由于您没有比较相同的操作,因此您将得到不同的结果。

的行为double在不同语言中完全相同,因为它在每种情况下都使用硬件来执行这些操作。唯一的区别是您选择显示结果的方法。

在 Java 中,如果你运行

double d = 0;
for (int i = 1; i <= 10; i++)
    System.out.printf("%f%n", d += 0.1);

它打印

0.100000
0.200000
0.300000
0.400000
0.500000
0.600000
0.700000
0.800000
0.900000
1.000000

如果你跑

double d = 0;
for (int i = 0; i < 8; i++) d += 0.1;
System.out.println("Summing 0.1, 8 times " + new BigDecimal(d));
System.out.println("How 0.8 is represented " + new BigDecimal(0.8));

你得到

Summing 0.1, 8 times 0.79999999999999993338661852249060757458209991455078125
How 0.8 is represented 0.8000000000000000444089209850062616169452667236328125
于 2012-01-17T14:29:19.217 回答
2
  1. 由于打印例程的行为方式。0.1不能以二进制浮点格式精确表示。
  2. 在 Python 中:

    >>> print('%.20f' % (.1 * 8))
    0.80000000000000004441
    >>> d = .0
    >>> for i in xrange(10):
    ...  d += .1
    ...  print('%.20f' % d)
    ... 
    0.10000000000000000555
    0.20000000000000001110
    0.30000000000000004441
    0.40000000000000002220
    0.50000000000000000000
    0.59999999999999997780
    0.69999999999999995559
    0.79999999999999993339
    0.89999999999999991118
    0.99999999999999988898
    

    但请注意:

    >>> print('%.20f' % .8)
    0.80000000000000004441
    
于 2012-01-17T14:28:04.187 回答