Ответ 1
Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой
Недавно я наткнулся на ошибку/функцию на нескольких языках. У меня есть очень базовые знания о том, как это было вызвано (и я хотел бы получить подробное объяснение), но когда я думаю обо всех ошибках, которые я должен был сделать за эти годы, вопрос в том, как определить " Эй, это может вызвать непонятную ошибку, лучше использовать произвольные функции точности, что на других языках есть эта ошибка (и те, кто этого не делает, почему). Кроме того, почему 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 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
Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой
Это не проблема языка. Это общая проблема с арифметикой с плавающей точкой.
Прекратить использование поплавков. Нет, действительно.
число чисел с плавающей запятой не является точным.
В Python int обрезает floats в направлении нуля до ближайшего целого.
(int)
в PHP, parseInt
в Javascript и to_i
в Ruby сделать то же самое.
Это не ошибка; это то, как работают эти функции.
Например, из docs для Python int
:
Преобразование чисел с плавающей запятой для целых чисел усекает (к нулю).
Это известная проблема, связанная с представлением с плавающей запятой, из которой вы можете найти здесь дополнительную информацию:
http://en.wikipedia.org/wiki/IEEE_754-2008
Конкретная проблема заключается в том, что 7.9 будет преобразован напрямую (trunc) в 7 при преобразовании его в int. В Python вы можете решить это с помощью
int( round(((0.1+0.7)*10)) )
... и аналогично на других языках.
Но да, это может быть проблемой во многих ситуациях. Числа с плавающей точкой недостаточно надежны для программ расчета заработной платы, например.
Возможно, другие могут дать вам другие подсказки. Hpe это помогает, в любом случае.
Используйте модуль decimal
:
>>> int((decimal.Decimal('0.1')+decimal.Decimal('0.7'))*10)
8
PHP использует числа с плавающей запятой по умолчанию, вам нужно вручную отбрасывать целые числа.
Вы должны знать о арифметике с плавающей запятой. Другие сообщения здесь содержат достаточно ссылок.
Лично я использую round/ceil/float в зависимости от того, что я ожидаю, в отличие от int
$a = (int) round((0.7 + 0.1) * 10);