рубиновые ошибки с плавающей точкой - PullRequest
10 голосов
/ 30 октября 2010

Может кто-нибудь объяснить, почему умножение на 100 здесь дает менее точный результат, а умножение на 10 дважды дает более точный результат?

± % sc
Loading development environment (Rails 3.0.1)
>> 129.95 * 100
12994.999999999998
>> 129.95*10
1299.5
>> 129.95*10*10
12995.0

Ответы [ 3 ]

25 голосов
/ 30 октября 2010

Если вы выполняете вычисления вручную в двоичном формате с двойной точностью, который ограничен 53 значащими битами, вы увидите, что происходит:

129,95 = 1,0000001111100110011001100110011001100110011001100110 x 2 ^ 7

129,95 * 100 = 1,1001011000010111111111111111111111111111111111111111111111111111011 x 2 ^ 13

Длина 56 значащих битов, округленная до 53 бит,

1.1001011000010111111111111111111111111111111111111111111111111111 x 2 ^ 13, что равно

12994.999999999998181010596454143524169921875

Теперь 129,95 * 10 = 1,0100010011011111111111111111111111111111111111111111111111111111111 x 2 ^ 10

Это длина 54 значащих бита, поэтому округляется до 53 бит, это 1.01000100111 x 2 ^ 10 = 1299,5

Теперь 1299,5 * 10 = 1,1001011000011 x 2 ^ 13 = 12995.

4 голосов
/ 30 октября 2010

Прежде всего: вы смотрите на строковое представление результата, а не на сам фактический результат. Если вы действительно хотите сравнить два результата, вы должны явно отформатировать оба результата, используя String#%, и вы должны отформатировать оба результата одинаково.

Во-вторых, именно так работают двоичные числа с плавающей запятой. Они неточны, они конечны и они двоичны. Все три означают, что вы получаете ошибки округления, которые обычно выглядят совершенно случайными, если только вы случайно не запомнили IEEE754 полностью и не можете повторить его во сне.

2 голосов
/ 30 октября 2010

Нет числа с плавающей запятой, точно равного 129.95. Таким образом, ваш язык использует значение, которое близко к нему. Если это значение умножить на 100, результат будет близок к 12995, но он, как оказалось, не равен 12995. (Он также не точно равен 100-кратному исходному значению, которое он использовал вместо 129.95.) интерпретатор печатает десятичное число, которое близко (но не равно) к значению 129.95 * 100 и которое показывает вам, что оно не совсем 12995. Также бывает, что результат 129.95 * 10 точно равен 1299,5. Это в основном удача.

Суть в том, что нельзя ожидать равенства из арифметики с плавающей запятой, только "близость".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...