2009-09-20 2 views
6

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

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

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

ответ

1

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

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

result=BigDecimal.new("7.3")-BigDecimal("7.2") 
puts "%2.2f" % result 
+0

Об этом можно говорить довольно хорошо: http://whynotwiki.com/Ruby_/_Numbers –

+2

Обратите внимание, что BigDecimal дает только точные результаты, если число имеет конечное число цифр в основание 10. – sepp2k

+0

http://floating-point-gui.de/ –

3

Проблема в том, что floating point is inaccurate. Вы можете решить это с помощью Rational, BigDecimal или просто простых целых чисел (например, если вы хотите хранить деньги, вы можете сохранить количество центов как int вместо количества долларов в качестве float).

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

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

0

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

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

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

Это было предложено до сюда, вы можете захотеть взглянуть на некоторые из ответов приведены выше, такие, как этот: Dealing with accuracy problems in floating-point numbers

6

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

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

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

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

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

1

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

Смежные вопросы