【问题标题】:How do I round a float to a specified number of significant digits in Ruby?如何在 Ruby 中将浮点数舍入到指定数量的有效数字?
【发布时间】:2011-12-05 08:24:02
【问题描述】:

如果在 Ruby 中有一个与 R's signif 等效的函数,那就太好了。

例如:

>> (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

【问题讨论】:

标签: ruby floating-point significant-digits


【解决方案1】:

可能有更好的方法,但这似乎工作正常:

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

【讨论】:

  • 太棒了!它是 printf (a.k.a sprintf, a.k.a %) 的“g”说明符,然后转换为浮点数!太棒了!
  • 这个实现应该内置到 Ruby 中。有人愿意提交补丁吗?
  • 等等。细微差别: (5.55).signif(2) # 应该 => 5.6 但给出 5.5
  • 我已经完成了对 ruby​​ 1.9 (github.com/alevchuk/ruby/commit/bc2253) 的良好拉取请求的一半以上!但需要照顾 (5.55).signif(2) # => 5.6
  • 我正在考虑采用/集成来自 R 的代码,svn.r-project.org/R/trunk/src/nmath/fprec.c 到 Ruby 的 numeric.c
【解决方案2】:

这是一个不使用字符串或其他库的实现。

class Float
  def signif(digits)
    return 0 if self.zero?
    self.round(-(Math.log10(self).ceil - digits))
  end
end

【讨论】:

    【解决方案3】:

    之前的一些答案和 cmets 都提到了这个解决方案,但这对我有用:

    # 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
    

    【讨论】:

      【解决方案4】:

      我在 Float 中没有看到类似的东西。 Float 主要是原生 double 类型的包装器,考虑到常见的二进制/十进制问题,我对 Float 不允许您操作有效数字并不感到惊讶。

      但是,标准库中的BigDecimal 确实可以理解有效数字,但是我再次看不到任何可以让您直接更改 BigDecimal 中的有效数字的东西:您可以要求但不能更改它.但是,您可以通过使用无操作版本的 multadd 方法来解决这个问题:

      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 会更慢,但如果您需要细粒度控制或需要避免常见的浮点问题,它可能是您唯一的选择。

      【讨论】:

      • BigDecimal#new 采用第二个参数来限制数字的有效位数。
      • @jwadsa​​ck:但这仅在创建新 BigDecimal 时有效,您无法更改现有 BigDecimal 的 digits 值。我认为这就是我所说的“我没有看到任何可以让您直接更改 BigDecimal 中的有效数字的东西”。
      【解决方案5】:

      您可能正在寻找 Ruby 的Decimal

      然后你可以写:

      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

      gem install ruby-decimal
      

      【讨论】:

        【解决方案6】:

        @Blou91 的答案几乎就在那里,但它返回一个字符串,而不是一个浮点数。这对我有用:

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

        所以作为一个函数,

        def round(val, sig_figs)
          (sprintf "%.#{sig_figs}f", val).to_f
        end
        

        【讨论】:

        • 这会四舍五入到特定的小数位数,但我们想要的是四舍五入到特定的有效数字位数。
        【解决方案7】:

        如果要打印尾随零,请使用 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 >
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多