2017-02-21 26 views
-2

Я просмотрел: Why can't decimal numbers be represented exactly in binary? и Why Are Floating Point Numbers Inaccurate?рубин округления и форматирования десятичных чисел

Мой вопрос, когда я округлить и формат чисел с плавающей точкой, используя рубин, результаты по-прежнему отличается от результата делать математику купить руку , Ниже приведен пример этого:

Edit 2

[28] pry(main)> ##################################### 
[29] pry(main)> # test one 
[30] pry(main)> ##################################### 
[31] pry(main)> foo = (6.0135 * (650000/1000)) 
      => 3908.7749999999996 
[32] pry(main)> foo = '%.2f' % foo.round(2) 
      => "3908.77" 
[33] pry(main)> # should be 3908.78 

[36] pry(main)> ##################################### 
[37] pry(main)> # test two 
[38] pry(main)> ##################################### 
[39] pry(main)> foo = 650000/1000 
      => 650 
[40] pry(main)> foo = foo * 6.0135 
      => 3908.7749999999996 
[41] pry(main)> foo = '%.2f' % foo.round(2) # should be 3908.78 
      => "3908.77" 

[44] pry(main)> ##################################### 
[45] pry(main)> # test three 
[46] pry(main)> ##################################### 
[47] pry(main)> foo = 650000/1000 
      => 650 
[48] pry(main)> foo = foo * 6.0135 
      => 3908.7749999999996 
[49] pry(main)> foo = foo.round(2) # should be 3908.78 
=> 3908.77 

[52] pry(main)> ##################################### 
[53] pry(main)> # test four - The result of test four is expected 
[54] pry(main)> ##################################### 
[55] pry(main)> foo = 650000/1000 
      => 650 
[56] pry(main)> foo = foo * 6.0135 
      => 3908.7749999999996 
[57] pry(main)> foo = '%.2f' % foo 
      => "3908.77" 

[58] pry(main)> ##################################### 
[59] pry(main)> # test five 
[60] pry(main)> ##################################### 
[61] pry(main)> foo = 650000/1000 
      => 650 
[62] pry(main)> foo = foo * 6.0135 
      => 3908.7749999999996 
[63] pry(main)> foo = foo.round(5) 
      => 3908.775 

Тест 1: Это нормальная формула я использую в моей библиотеке, которая дает мне этот вопрос.

Тест 2: Моя мысль здесь в том, что, возможно, делает обе операции в одном уступке вызывает некоторые проблемы

Test 3: Ну так '%.2f' %, как известно, вызывает некоторые проблемы округления, где он обрезает вещи, может быть, это вопросы.

Тест 4: С '%.2f' % не проблема, может быть, проблема .round(2).

Edit 2: Тест 5: Если я расширяю цифры, что вокруг смотрит на меня получить номер, который я мог бы работать с (т.е. круг (2) следует, что число раз.). Но это похоже на решение, которое работает в ограниченном случае, а не вообще

Мне все равно, что плавающая точка неточна. Мой вопрос: как я могу правильно округлить это число, чтобы получить правильный ответ (ответ вы получите, если вы выполнили операцию вручную).

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

спасибо.

Edit 1

Если вы используете .round(5) вместо .round(2) вы получите более разумный ответ, когда сравнивая его с тем, что ответ должен быть. Есть ли недостатки в увеличении числа внутри раунда?

+0

Почему downvote? –

+1

Этот раунд верен. 3908.77499999 .. всегда будет 3908.77, потому что он действительно меньше 3908.775. Вы всегда округлите общее значение, вы не начинаете на последней цифре раунда, а затем идите ... –

+0

Помимо того факта, что фактический ответ должен быть 3908.78. Если вы используете '.round (5)', вы получаете 3908.78 –

ответ

3

round работает должным образом. Вы получаете неправильный результат, потому что ваш ввод уже испорчен. Число с плавающей точкой 6.0135 на самом деле:

6.01349999999999962341235004714690148830413818359375 

Умножив это число 650 делает ошибки хуже. Вы получаете результат, который ближе к 3908.77 чем 3908.78:

foo = 6.0135 * 650 
#=> 3908.7749999999996 

(foo - 3908.77).abs 
#=> 0.004999999999654392 

(foo - 3908.78).abs 
#=> 0.005000000000563887 

Чтобы получить правильный результат, вы могли бы использовать что-то вроде этого:

foo = (6.0135 * 10000).round * 0.065 
#=> 3908.775 

foo.round(2) 
#=> 3908.78 

Или вы могли бы использовать BigDecimal, чтобы избежать плавающей ошибки точки в первую очередь:

require 'bigdecimal' 
foo = BigDecimal('6.0135') * 650 
foo.to_s('F') 
#=> "3908.775" 

foo.round(2).to_s('F') 
#=> "3908.78" 
+0

Спасибо. Есть ли основные сокращения назад к большому децималу, кроме какого-то удара производительности? Я намерен делать такую ​​математику 25 раз за запрос, будет ли это изменение сильно повлиять на выполнение запроса? –

+1

@alex_milhouse цикл запроса-ответа в Rails не такой легкий. Я был бы удивлен, если бы 25 расчетов повлияли бы на это заметно. – Stefan

+0

отлично. Спасибо, это было именно то, что мне нужно. –

0

'%.2f' % - обрезает число и не правильно

.round(2) закруглить - не выглядит достаточно знаков после запятой назад, чтобы правильно округлить. Поэтому, если вы сделаете число внутри больше чем 2, вопрос округления исчезнет.

Edit 1

Есть 5 способов округлить число в соответствии с IEEE:

  1. до ближайшего, связи с даже
  2. к ближайшему, галстуки от нуля
  3. к 0
  4. в направлении + ∞
  5. в направлении -∞

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

+1

разделить личность? –

+1

Я думаю, что вам нужно будет обосновать эти утверждения, сославшись на стандарты с плавающей точкой IEEE, прежде чем это будет квалифицироваться как ответ. –

+0

Да @muistooshort и lukasz У меня на самом деле возникают мысли о том, как я «решил» это. Хотя это дает мне ответ, который я хотел для этого конкретного случая. Есть ли другие проблемы с этим. –

0

Я испытал все это на моей Rails консоли, и не может видеть странное поведение:

>> foo = 650000/1000 
=> 650 
>> foo = foo * 6.0135 
=> 3908.7749999999996 
>> foo.round(2) 
=> 3908.77 
>> foo.round(3) 
=> 3908.775 
>> foo.round(5) 
=> 3908.775 
>> afoo = '%.2f' % foo 
=> "3908.77" 
>> afoo = '%.5f' % foo 
=> "3908.77500" 

3908,7749999999996 меньше, чем 3908.775 и будет округлено до 3908.77, если вы используете .round (2). .round (5) Результаты, ожидаемые в 3908.775, которые также могут быть воспроизведены с помощью калькулятора Google. Почему вы думаете, что это должно быть неверно? Я бы очень хотел это понять.

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