【问题标题】:How to round Decimals to the First Significant Figure in Ruby如何在 Ruby 中将小数四舍五入到第一个有效数字
【发布时间】:2019-07-20 14:37:02
【问题描述】:

我正在尝试解决与个人项目相关的任务的边缘案例。

确定服务的单价,由total_amountcost组成。

示例包括:

# 1
unit_price = 300 / 1000 # = 0.3

# 2
unit_price = 600 / 800 # = 0.75

# 3
unit_price = 500 / 1600 # = 0.3125

对于 1 和 2,unit_prices 可以保持不变。对于 3,四舍五入到小数点后 2 位就足够了,例如(500 / 1600).round(2)

浮动变长时出现问题:

# 4
unit_price = 400 / 56000 # = 0.007142857142857143

很明显,浮动是相当长的。在这种情况下,目标是四舍五入到第一位有效数字。

我考虑过使用正则表达式来匹配第一个非零小数,或者找到第二部分的长度并应用一些逻辑:

  • unit_price.match ~= /[^.0]/
  • unit_price.to_s.split('.').last.size

欢迎任何帮助

【问题讨论】:

  • 请定义“第一重要”是什么意思 你期望400 / 56000 # = 0.007 吗?
  • 应该提到这一点。是的,在那个例子中,它是0.007
  • 旁注: 400 / 56000 在 ruby​​ 中是 0400.0 / 56000 是带小数的浮点数。

标签: ruby-on-rails ruby algorithm decimal


【解决方案1】:

这种计算应该使用BigDecimal

require 'bigdecimal'

bd = BigDecimal((400.0 / 56000).to_s)
#⇒ 0.7142857142857143e-2
bd.exponent
#⇒ -2

例子:

[10_000.0 / 1_000, 300.0 / 1_000, 600.0 / 800,
                   500.0 / 1_600, 400.0 / 56_000].
  map { |bd| BigDecimal(bd.to_s) }.
  map do |bd|
    additional = bd.exponent >= 0 ? 0 : bd.exponent + 1 
    bd.round(2 - additional) # THIS
  end.
  map(&:to_f)
#⇒ [10.0, 0.3, 0.75, 0.31, 0.007]

【讨论】:

    【解决方案2】:

    您可以使用正则表达式检测零字符串的长度。这有点难看,但它有效:

    def significant_round(number, places)
      match = number.to_s.match(/\.(0+)/)
      return number unless match
      zeros = number.to_s.match(/\.(0+)/)[1].size
      number.round(zeros+places)
    end  
    
    pry(main)> significant_round(3.14, 1)
    => 3.14
    pry(main)> significant_round(3.00014, 1)
    => 3.0001
    

    【讨论】:

    • 它在小数点后没有零的浮点数上引发(如3.14)顺便说一句。
    • 坦率地说,我怀疑3.00014 应该保留0001。这个1 绝不是一个有效的数字。
    【解决方案3】:
    def my_round(f)
      int = f.to_i
      f -= int
      coeff, exp = ("%e" % f).split('e')
      "#{coeff.to_f.round}e#{exp}".to_f + int
    end
    

    my_round(0.3125)
      #=> 0.3 
    my_round(-0.3125)
      #=> -0.3 
    my_round(0.0003625)
      #=> 0.0004 
    my_round(-0.0003625)
      #=> -0.0004 
    my_round(42.0031)
      #=> 42.003 
    my_round(-42.0031)
      #=> -42.003 
    

    步骤如下。

    f = -42.0031
    int = f.to_i
      #=> -42 
    f -= int
      #=> -0.0031000000000034333 
    s = "%e" % f
      #=> "-3.100000e-03" 
    coeff, exp = s.split('e')
      #=> ["-3.100000", "-03"] 
    c = coeff.to_f.round
      #=> -3 
    d = "#{c}e#{exp}"
      #=> "-3e-03" 
    e = d.to_f
      #=> -0.003 
    e + int 
      #=> -42.003 
    

    要改为只保留舍入后的最高有效位,请将方法更改为以下。

    def my_round(f)
      coeff, exp = ("%e" % f).split('e')
      "#{coeff.to_f.round}e#{exp}".to_f
    end
    

    如果f <= 0,这将返回与之前的方法相同的结果。这是f > 0时的示例:

    my_round(-42.0031)
      #=> -40.0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-25
      • 2010-09-14
      相关资源
      最近更新 更多