Почему Ruby добавляет точность / цифры при расчете мода?
Мне нужно сделать некоторые вычисления с координатами и натолкнулся на это (по крайней мере для меня) странное поведение. Может кто-нибудь объяснить, почему это происходит?
$ long
=> 49.0126760222489
$ long % long.floor
=> 0.012676022248896857
Я ожидал, что последняя строка будет содержать только цифры после периода, от long
, но вместо этого есть дополнительный 6857
прикрепленный к концу.
Почему он добавляет цифры?
Откуда поступает информация?
2 ответа
Я предполагаю, что мы просто видим впечатления поплавка. См. То есть 0.0126760222489 % 1.0
а также 1.0126760222489 % 1.0
, Вы могли бы подумать, что результат должен быть таким же, но нет - IEEE754 с плавающей запятой / двойным не гарантирует идеальных результатов, и по умолчанию они используются в Ruby для хранения значений с плавающей запятой.
Это даже несколько показано в документах
6543.21.modulo(137) #=> 104.21
6543.21.modulo(137.24) #=> 92.9299999999996
Вы можете видеть, что второй результат имеет небольшую ошибку. На самом деле, на Ruby 2.3.1 я запустил первую строку и получил:
pry(main)> 6543.21.modulo(137)
=> 104.21000000000004
Это не обязательно связано с модулем, и не всегда видно:
[30] pry(main)> 10.0126760222489 - 0.0
=> 10.0126760222489
[31] pry(main)> 10.0126760222489 - 1.0
=> 9.0126760222489
[32] pry(main)> 10.0126760222489 - 2.0
=> 8.0126760222489
[33] pry(main)> 10.0126760222489 - 3.0
=> 7.0126760222489
[34] pry(main)> 10.0126760222489 - 4.0
=> 6.0126760222489
[35] pry(main)> 10.0126760222489 - 5.0
=> 5.0126760222489
[36] pry(main)> 10.0126760222489 - 6.0
=> 4.0126760222489
[37] pry(main)> 10.0126760222489 - 7.0
=> 3.0126760222489004
Каждое программное обеспечение, которое использует стандартные операции с плавающей запятой, должно учитывать эти небольшие ошибки. Если вы не можете справиться с этим по какой-то причине, то вы можете использовать bigdecimal
(должен быть уже включен в ваш Ruby), с фиксированной запятой или какой-либо подобной числовой библиотекой
require 'bigdecimal'
BigDecimal('6543.21').modulo(137).to_s
=> "0.10421E3"
BigDecimal('6543.21').modulo(137).to_f
=> 104.21
Имейте в виду, что "bigdecimal" может быть медленнее и использовать больше памяти.
Кажется, это не имеет ничего общего с самим Ruby, но:
Числа с плавающей запятой, которые обычно страдают от этого типа ошибки округления, потому что они ограничены числом х байтов и, в общем, не могут хранить десятичное число идеально.
Из вышеприведенных документов также можно получить несколько вариантов обхода поведения с плавающей точкой. Приведенная выше документация может рассматриваться как официальная, поскольку она предоставлена ruby-doc.org.