Арифметика в рубине - PullRequest
       40

Арифметика в рубине

6 голосов
/ 20 сентября 2009

Почему этот код 7.30 - 7.20 в ruby ​​возвращает 0.0999999999999996, а не 0.10?

Но если я напишу, например, 7.30 - 7.16, все будет хорошо, я получу 0.14.

В чем проблема и как я могу ее решить?

Ответы [ 6 ]

20 голосов
6 голосов
/ 20 сентября 2009

Проблема в том, что некоторые числа, которые мы можем легко записать в десятичном виде, не имеют точного представления в конкретном формате с плавающей запятой, реализованном текущим оборудованием. Случайный способ заявить это - все целые числа, но не все дроби, потому что мы обычно храним дробь с показателем 2**e. Итак, у вас есть 3 варианта:

  1. Закруглите соответственно. Необоснованный результат всегда очень близок, поэтому округленный результат неизменно «идеален». Это то, что делает Javascript, и многие люди даже не осознают, что JS делает все с плавающей запятой.

  2. Использовать арифметику с фиксированной точкой. Руби на самом деле делает это действительно легко; это один из немногих языков, который плавно переходит на Class Bignum из Fixnum по мере увеличения чисел.

  3. Используйте класс, предназначенный для решения этой проблемы, например BigDecimal

Чтобы рассмотреть проблему более подробно, мы можем попытаться представить ваш «7.3» в двоичном виде. 7 часть легка, 111, но как нам сделать .3? 111,1 - 7,5, слишком большой, 111,01 - 7,25, приближается. Оказывается, 111.010011 является «следующим ближайшим меньшим числом», 7.296875, и когда мы пытаемся заполнить пропущенный .003125, в конечном итоге мы обнаруживаем, что это просто 111.010011001100110011 ... навсегда, не представляемое в выбранной нами кодировке в конечной битовой строке. .

3 голосов
/ 20 сентября 2009

Проблема в том, что с плавающей точкой является неточным . Вы можете решить эту проблему, используя Rational, BigDecimal или просто простые целые числа (например, если вы хотите хранить деньги, вы можете хранить количество центов в виде целого числа вместо числа долларов в виде числа с плавающей запятой).

BigDecimal может точно хранить любое число с конечным числом цифр в базе 10 и округляет числа, которые не имеют (поэтому три трети не являются одним целым).

Rational может точно хранить любое рациональное число и вообще не может хранить иррациональные числа.

1 голос
/ 21 сентября 2009

Интересно отметить, что число с несколькими десятичными знаками в одной базе может обычно иметь очень большое количество десятичных знаков в другой. Например, требуется бесконечное количество десятичных знаков, чтобы выразить 1/3 (= 0,3333 ...) в основании 10, но только один десятичный в основании 3. Аналогично, требуется много десятичных знаков, чтобы выразить число 1/10 ( = 0,1) в базе 2.

1 голос
/ 20 сентября 2009

Это распространенная ошибка в представлении чисел с плавающей запятой в памяти.

Используйте BigDecimal, если вам нужны точные результаты.

result=BigDecimal.new("7.3")-BigDecimal("7.2")
puts "%2.2f" % result
0 голосов
/ 20 сентября 2009

Поскольку вы выполняете математические операции с плавающей запятой, возвращаемое число - это то, что ваш компьютер использует для точности.

Если вы хотите получить более точный ответ с заданной точностью, просто умножьте на него число с плавающей точкой (например, на 100), преобразовайте его в целое число, выполните математические вычисления, а затем разделите.

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

Это уже спрашивалось здесь, вы можете поискать ответы на некоторые вопросы, данные ранее, такие как этот: Решение проблем с точностью в числах с плавающей запятой

...