2009-12-23 2 views
2

Я понимаю из-за неточного представления плавающих точек, следующий код «чувствует» непоследовательность.Согласованное округление плавающих точек в Ruby

"%.1f" % 1.14 # => 1.1 
"%.1f" % 1.15 # => 1.1 
"%.1f" % 1.16 # => 1.2 
"%.0f" % 1.4 # => 1 
"%.0f" % 1.5 # => 2 
"%.0f" % 1.6 # => 2 

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

ответ

5

Если вы хотите десятичную точность, используйте BigDecimal вместо поплавков.

Edit: Вы должны вручную круглое число до нужной длины перед передачей его %, в противном случае он преобразуется в обычный поплавок, прежде чем округлое.

"%.1f" % BigDecimal('1.15').round(1) => "1.2" 
"%.0f" % BigDecimal('1.5').round(0) => "2" 
+0

Да, простой и элегантный. – bryantsai

+0

вопрос стороны, x.to_s для float всегда дает x (например, 1.15.to_s => '1.15')? или также повлияет на неточное представление? – bryantsai

+0

Извините, я не уверен в этом. Из моих тестов все, кажется, проходит через ОК, однако Float # to_s, похоже, усекает любое число с более чем 15 цифрами. –

0

Умножить на 100, то круглый, а затем разделить на 100:

(1.15 * 100).round/100.0 # => 1.15 

Это не совсем элегантно, но он избегает использования строк.

+0

это не правильно - проблема все еще существует, потому что '1.15' теперь представлен внутренне и неточно с использованием плавающей запятой. Опять же, как только вы придете, чтобы отобразить его, вы получите ответы, которых вы не ожидаете. – Peter

+0

Ack! Конечно, ты прав. Не знаю, о чем я думал ... –

2

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

Например,

x = 1.15 
"%.1f" % (1.000001*x) # include correction for imprecise floating-point. 

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

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

+0

Разве это не повлияло бы на какое-то значение чуть-чуть .5? – bryantsai

+0

Ну, значение '(1/1.000001) * (1.1499999999)' будет неправильно округлено, но, видя, что вы только печатаете значения для отображения, я думаю, что это нормально. Если это проблема, просто добавьте еще несколько нулей. – Peter

1

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

def roundthis(x, m) 
    return (x/m+0.50001).floor*m 
end 

for x in [1.14, 1.15, 1.16] 
    print "#{x} #{roundthis(x, 0.1)} \n" 
end 

for x in [1.4, 1.5, 1.6] 
    print "#{x} #{roundthis(x, 1.0)} \n" 
end 

Это, положить в файл с именем roundtest.rb и выполненные отпечатки

bash> ruby roundtest.rb 
1.14 1.1 
1.15 1.2 
1.16 1.2 
1.4 1.0 
1.5 2.0 
1.6 2.0 

Примечание легкость округления до ближайшего 2, 15, 0,005, или любой другой.

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