Арифметика в ruby
почему этот код 7.30 - 7.20
в ruby возвращается 0.0999999999999996
, а не 0.10
?
но если я напишу 7.30 - 7.16
, например, все будет хорошо, я получу 0.14
.
что проблема, и как я могу ее решить?
6 ответов
проблема в том, что некоторые числа, которые мы можем легко записать в десятичном формате, не имеют точного представления в конкретном формате с плавающей запятой, реализованном текущим оборудованием. Случайный способ заявить об этом заключается в том, что все целые числа, но не все дроби, потому что мы обычно храним дробь с 2**e
экспонента. Итак, у вас есть 3 варианта:
округлить соответствующим образом. Неокругленный результат всегда очень близок, поэтому округленный результат неизменно "отлично". Это то, что делает Javascript, и многие люди даже не понимают, что JS делает все в плавающей точке.
использовать арифметику с фиксированной точкой. Ruby на самом деле делает это очень легко; это один из единственных языков, который плавно переходит в класс Bignum из Fixnum по мере увеличения чисел.
используйте класс, который предназначен для решения этой проблемы, например
BigDecimal
смотреть на проблему в более подробно, мы можем попытаться представить ваш " 7.3 " в двоичном формате. 7 часть проста, 111, но как нам это сделать?3? 111.1-7.5, слишком большой, 111.01-7.25, приближается. Оказывается, 111.010011 - это "следующее ближайшее меньшее число", 7.296875, и когда мы пытаемся заполнить недостающее .003125 в конце концов мы узнаем, что это всего лишь 111.010011001100110011... навсегда, не представимо в выбранной нами кодировке в конечной битовой строке.
проблема в том, что плавающая точка неточна. Вы можете решить его, используя рациональные, BigDecimal или просто целые числа (например, если вы хотите хранить деньги, вы можете хранить количество центов как int вместо количества долларов в виде поплавка).
BigDecimal может точно хранить любое число, которое имеет конечное число цифр в базе 10 и округляет числа, которые этого не делают (поэтому три трети не являются одним целым).
Rational может точно хранить любые рациональное число и не может хранить иррациональные числа вообще.
Это распространенная ошибка от того, как числа с плавающей запятой представлены в памяти.
используйте bigdecimal если вам нужны точные результаты.
result=BigDecimal.new("7.3")-BigDecimal("7.2")
puts "%2.2f" % result
интересно отметить, что число, которое имеет несколько десятичных знаков в одной базе, как правило, имеют очень большое количество десятичных знаков в другой. Например, для выражения 1/3 (=0,3333) требуется бесконечное число десятичных знаков...) в базе 10, но только один десятичный в базе 3. Аналогично, для выражения числа 1/10 (=0.1) в базе 2 требуется много десятичных знаков.
поскольку вы делаете математику с плавающей запятой, то возвращаемое число-это то, что ваш компьютер использует для точности.
Если вы хотите получить более близкий ответ, с заданной точностью, просто кратный поплавку (например, на 100), преобразуйте его в int, выполните математику, а затем разделите.
есть и другие решения, но я считаю это самым простым, так как округление всегда кажется мне немного сомнительным.
Это было задано раньше здесь, вы можете искать некоторые из ответы, данные ранее, такие как этот: решение проблем точности в числах с плавающей запятой