2012-01-17 5 views
2

Допустим, у нас есть этот вид петли (псевдокод)Точность десятичного числа в Java, C, C++ и другие

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. Как это ведет себя на других языках?
+1

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems – yas4891

+0

Это не десятичные числа, они двоичные. Изучите это хорошо. –

+0

StackOverflow нуждается в байесовском фильтре, который автоматически ищет слова «Decimal» и «Precision» в новых сообщениях ... – mcfinnigan

ответ

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 
+1

Означает ли это, что на каждом языке двойное значение 0.8 сохраняется как 0.79999999999999? Потому что это значение __ugly__, которое я могу видеть в режиме отладки в представлении переменной. – user219882

+0

@ Томас: то, как вы вычисляете '.8' yield' .79999999999999993339', но если вы просто введете литерал, вы получите '.80000000000000004441'. –

+0

Нет, значение 0,8 не является точным, но сумма или 0,1 + 0,1 + ... 0,1 немного меньше, чем представление для 0,8. См. Мое редактирование. –

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 
    
+0

Re, мой [теперь удаленный] ответ, да, вы правы. – Joe

6

Как ответил here, это не относится ни к одному языку.

Смотрите здесь: What Every Programmer Should Know About Floating-Point Arithmetic

Вещественные числа бесконечны. Компьютеры работают с конечным числом бит (32 бита, 64 бита сегодня).В результате арифметика с плавающей запятой, выполняемая компьютерами, не может представлять все реальные числа. 0,1 - одно из этих чисел.

Обратите внимание, что это не проблема, связанная с Ruby, но ко всем языкам программирования, поскольку она исходит из того, как компьютеры представляют действительные числа.

+0

+1 для ссылки на бумагу Голдберга. –

Смежные вопросы