int ((0,1 + 0,7) * 10) = 7 на нескольких языках. Как это предотвратить? - PullRequest
13 голосов
/ 22 июня 2011

Недавно я столкнулся с ошибкой / особенностью на нескольких языках. У меня есть базовые знания о том, как это вызвано (и я хотел бы получить подробное объяснение), но когда я вспоминаю все ошибки, которые я должен был сделать за эти годы, возникает вопрос: как я могу определить " * Эй, это может вызвать нелепую ошибку, я бы лучше использовал функции произвольной точности ", какие ошибки есть у других языков (и у тех, у кого нет, почему ) Кроме того, почему 0,1 + 0,7 это делает, а 0,1 + 0,3 нет, есть ли другие известные примеры?

PHP

//the first one actually doesn't make any sense to me,
//why 7 after typecast if it's represented internally as 8?
debug_zval_dump((0.1+0.7)*10); //double(8) refcount(1)
debug_zval_dump((int)((0.1+0.7)*10)); //long(7) refcount(1)
debug_zval_dump((float)((0.1+0.7)*10)); //double(8) refcount(1)

Python:

>>> ((0.1+0.7)*10)
7.9999999999999991
>>> int((0.1+0.7)*10)
7

Javascript:

alert((0.1+0.7)*10); //7.999999999999999
alert(parseInt((0.7+0.1)*10)); //7

Ruby:

>> ((0.1+0.7)*10).to_i                                                  
=> 7                                                                    
>>((0.1+0.7)*10)                                                       
=> 7.999999999999999                                                    

Ответы [ 7 ]

7 голосов
/ 22 июня 2011

Это не проблема языка. Это общая проблема с арифметикой с плавающей точкой .

5 голосов
/ 22 июня 2011
3 голосов
/ 22 июня 2011

Представление чисел с плавающей запятой не является точным .

В Python, int усечения плавает в направлении нуля до ближайшего целого числа. (int) в PHP, parseInt в Javascript и to_i в Ruby делают то же самое.

Это не ошибка; это как работают эти функции.

Например, с документы для Python int:

Преобразование чисел с плавающей запятой до целых усечений (к нулю).

1 голос
/ 22 июня 2011

Используйте модуль decimal:

>>> int((decimal.Decimal('0.1')+decimal.Decimal('0.7'))*10)
8
1 голос
/ 22 июня 2011

Это известная проблема, связанная с представлением с плавающей запятой, из которой вы можете найти дополнительную информацию здесь:

http://en.wikipedia.org/wiki/IEEE_754-2008

Конкретная проблема заключается в том, что 7.9 будет напрямуюпреобразовал (trunc) в 7 при преобразовании его в int.В Python вы можете решить эту проблему с помощью:

int( round(((0.1+0.7)*10)) )

... и аналогично в других языках.

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

Возможно, другие могут дать вам другие советы.В любом случае, это помогает.

0 голосов
/ 22 июня 2011

PHP по умолчанию использует числа с плавающей запятой, вам нужно вручную приводить к целым числам.

Вы должны знать о арифметике с плавающей запятой.Другие посты здесь содержат достаточно ссылок об этом.

Лично я использую round / ceil / float в зависимости от того, что я ожидаю, в отличие от int

$a = (int) round((0.7 + 0.1) * 10);
...