Проблема с точностью математических операций Ruby

Знаете ли вы, как решить следующую проблему с математической точностью?

p RUBY_VERSION # => "1.9.1"
p 0.1%1 # => 0.1
p 1.1%1 # => 0.1
p 90.0%1 # => 0.0
p 90.1%1 # => 0.0999999999999943
p 900.1%1 # => 0.100000000000023

p RUBY_VERSION # => "1.9.2"
p 0.1%1 # => 0.1
p 1.1%1 # => 0.10000000000000009
p 90.0%1 # => 0.0
p 90.1%1 # => 0.09999999999999432
p 900.1%1 # => 0.10000000000002274

person Andrei    schedule 23.09.2010    source источник
comment
Это было задано 4000 раз, я думаю.   -  person Alexandre C.    schedule 23.09.2010
comment
возможный дубликат Точность с плавающей запятой   -  person duffymo    schedule 23.09.2010


Ответы (3)


Большой десятичный


Как сказал человек;

Сжатие бесконечного числа действительных чисел в конечное число битов требует приближенного представления.


Однако я добился больших успехов, используя ссылку класс BigDecimal. Цитировать его вступление

Ruby предоставляет встроенную поддержку целочисленной арифметики произвольной точности. Например:

42**13 -> 1265437718438866624512

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


Возьмем один из ваших примеров;

>> x = BigDecimal.new('900.1')
=> #<BigDecimal:101113be8,'0.9001E3',8(8)>
>> x % 1
=> #<BigDecimal:10110b498,'0.1E0',4(16)>
>> y = x % 1
=> #<BigDecimal:101104760,'0.1E0',4(16)>
>> y.to_s
=> "0.1E0"
>> y.to_f
=> 0.1


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

person Chris McCauley    schedule 23.09.2010
comment
Разум! Вы не просто повышаете точность: вы выбираете лучшее представление для десятичных чисел. 1/3 все равно будет округлено. - person xtofl; 23.09.2010
comment
@xtofl - это справедливое замечание, но я надеялся, что цитата о «приблизительном представлении» затронула его. - person Chris McCauley; 23.09.2010

Это верно для всех компьютерных языков, а не только для Ruby. Это особенность представления чисел с плавающей запятой на двоичных компьютерах:

Что должен знать каждый программист об арифметике с плавающей запятой

person duffymo    schedule 23.09.2010

Запись 0.1 в число с плавающей запятой всегда приводит к ошибкам округления. Если вам нужно «точное» десятичное представление, вам следует использовать тип Decimal.

person xtofl    schedule 23.09.2010
comment
Я не заметил такой проблемы в PHP. Есть ли веская причина, по которой Ruby не имеет «точного» десятичного представления по умолчанию? - person Andrei; 23.09.2010
comment
У PHP та же проблема, потому что дело не в языке: php.net/manual/ ru/language.types.float.php - person duffymo; 23.09.2010