Как округлить число с плавающей точкой до указанного числа значащих цифр в Ruby? - PullRequest
8 голосов
/ 05 декабря 2011

Было бы неплохо иметь в Ruby эквивалент функции знака R .

Например:

>> (11.11).signif(1)
10
>> (22.22).signif(2)
22
>> (3.333).signif(2)
3.3
>> (4.4).signif(3)
4.4 # It's usually 4.40 but that's OK. R does not print the trailing 0's
    # because it returns the float data type. For Ruby we want the same.
>> (5.55).signif(2)
5.6

Ответы [ 7 ]

13 голосов
/ 05 декабря 2011

Возможно, есть лучший способ, но, похоже, он работает нормально:

class Float
  def signif(signs)
    Float("%.#{signs}g" % self)
  end
end

(1.123).signif(2)                    # => 1.1
(11.23).signif(2)                    # => 11.0
(11.23).signif(1)                    # => 10.0
3 голосов
/ 25 ноября 2014

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

# takes in a float value and returns another float value rounded to 
# given significant figures.    
def round_to_sig_figs(val, sig_figs)
  BigDecimal.new(val, sig_figs).to_f
end
2 голосов
/ 03 июля 2017

Вот реализация, которая не использует строки или другие библиотеки.

class Float
  def signif(digits)
    return 0 if self.zero?
    self.round(-(Math.log10(self).ceil - digits))
  end
end
2 голосов
/ 05 декабря 2011

Вы, наверное, ищете Ruby's Десятичный .

Вы можете написать:

require 'decimal/shortcut'
num = 1.23541764
D.context.precision = 2
num_with_2_significant_digits = +D(num.to_s) #  => Decimal('1.2')
num_with_2_significant_digits.to_f #  => 1.2000000000000002

Или, если вы предпочитаете использовать тот же синтаксис, добавьте это как функцию в класс Float следующим образом:

class Float
  def signif num_digits
    require 'decimal/shortcut'
    D.context.precision = num_digits
    (+D(self.to_s)).to_f
  end
end

Тогда использование будет таким же, т.е.

 (1.23333).signif 3
 # => 1.23

Чтобы использовать его, установите гем

gem install ruby-decimal
2 голосов
/ 05 декабря 2011

Я не вижу ничего подобного в Float. Float - это в основном оболочка для нативного типа double, и учитывая обычные двоичные / десятичные ошибки, я не удивлен, что Float не позволяет вам манипулировать значащими цифрами.

Тем не менее, BigDecimal в стандартной библиотеке действительно понимает значащие цифры, но, опять же, я не вижу ничего, что позволяло бы вам напрямую изменять значащие цифры в BigDecimal: вы можете попросить об этом, но вы можете не меняй это. Но вы можете обойти это, используя безоперационную версию методов mult или add:

require 'bigdecimal'
a = BigDecimal.new('11.2384')
a.mult(1, 2) # the result is 0.11E2   (i.e. 11)
a.add(0, 4)  # the result is 0.1124E2 (i.e. 11.24)

Второй аргумент этих методов:

Если указано и меньше количества значащих цифр результата, результат округляется до этого количества цифр в соответствии с BigDecimal.mode.

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

0 голосов
/ 20 июня 2017

@ Ответ Blou91 почти готов, но он возвращает строку, а не число с плавающей точкой.Это ниже работает для меня:

(sprintf "%.2f", 1.23456).to_f

Так как функция,

def round(val, sig_figs)
  (sprintf "%.#{sig_figs}f", val).to_f
end
0 голосов
/ 08 июня 2014

Используйте sprintf, если вы хотите напечатать конечные нули

2.0.0-p353 :001 > sprintf "%.3f", 500
 => "500.000"
2.0.0-p353 :002 > sprintf "%.4f", 500
 => "500.0000"
2.0.0-p353 :003 >
...