【问题标题】:Truncate a floating point number without rounding up截断浮点数而不四舍五入
【发布时间】:2011-12-27 17:54:21
【问题描述】:

我有一个浮点数,我想将其截断为 3 个位置,但我不想四舍五入。

例如,将1.0155555555555555 转换为1.015(不是1.016)。

我将如何在 Ruby 中执行此操作?

【问题讨论】:

  • 负数应该发生什么,例如-1.01555 ?
  • @MarkByers 对于我正在做的事情,这个数字永远不会是负数。

标签: ruby math floating-point decimal precision


【解决方案1】:

假设你有一个float,试试这个:

(x * 1000).floor / 1000.0

结果:

1.015

在线查看:ideone

【讨论】:

  • 并非总是如此,例如 (128.653 * 1000) / 1000.0 => 128.652 # 不起作用 - 因为相乘后的值是 128652.999999
  • 这不是一个答案,因为它依赖于四舍五入,这是没有意义的。
【解决方案2】:

乘以一千,地板,除以一千,确保进行浮点除法。

(x * 1000).floor / 1000.0

或者,在 Ruby 1.9.2 中,使用早期版本中没有的 round 版本,

(x - 0.0005).round(3)

【讨论】:

  • 你不只是喜欢双打吗... pry(main)> (1.0155555 * 1000).floor / 1000.0 => 1.015 # 工作正常 pry(main)> (128.653 * 1000).floor / 1000.0 => 128.652 # 不起作用 - 因为相乘后的值是 128652.999999
【解决方案3】:

您也可以转换为 BigDecimal,并对其调用 truncate。

1.237.to_d.truncate(2).to_f # will return 1.23

【讨论】:

  • 谢谢,席德。这正是我一直在寻找的方法。
  • 干杯@ElliotLarson!
  • 59.99999999999999999999.to_d.truncate(2).to_f 上失败,返回60.0!!
  • @Anwar 那是因为对于常规浮点数来说精度太高了。甚至59.99999999999999999999.inspect 也是“60.0”。所以转换发生在to_d之前。
  • 大家好,当整数部分足够大时会出现精度故障。 111111111.9999999.to_d.truncate(2).to_f111111112.0。请参阅下面我的破解版本。干杯!
【解决方案4】:

sid 的答案很好,但它没有满足第一个要求,因此无法通过 Anwar 的测试。我们必须从 raw 开始,这样 ruby​​ 就不会轻易转换数字。并且以原始方式开始原始获取是使用纯字符串,所以

> "59.99999999999999999999".to_d.truncate(2) => #BigDecimal:55a38a23cd68,'0.5999E2',18(45)> > "59.99999999999999999999".to_d.truncate(2).to_s => "59.99" > "59.99999999999999999999".to_d.truncate(2).to_f => 59.99

现在就分享这个,因为我今天自己也遇到了这个问题:)

【讨论】:

    【解决方案5】:

    由于 ruby 2.4 Float#truncate 方法将多个十进制数字作为可选参数:

    1.0155555555555555.truncate(3)
    # => 1.015
    

    【讨论】:

      【解决方案6】:

      此解决方案基于 @SidKrishnan 的出色 BigDecimal 技巧,但也可以处理更大的浮点数而不会因精度问题而动摇。

      # Truncate a floating-point value without rounding up.
      #
      #   trunc_float(1.999, 2)   # => 1.99
      #   trunc_float(1.999, 0)   # => 1.0
      #
      # @param value [Float]
      # @param precision [Integer]
      # @return [Float]
      def trunc_float(value, precision)
        BigDecimal(value.to_s).truncate(precision).to_f
      end
      
      #--------------------------------------- Test
      
      describe ".trunc_float" do
        def call(*args)
          trunc_float(*args)
        end
      
        it "generally works" do
          [
            [[1, 0], 1.0],
            [[1.999, 4], 1.999],
            [[1.999, 3], 1.999],
            [[1.999, 2], 1.99],
            [[1.999, 1], 1.9],
            [[1.999, 0], 1.0],
            [[111111111.9999999, 3], 111111111.999],
            [[1508675846.650976, 6], 1508675846.650976],
          ].each do |input, expected|
            output = call(*input)
            expect([input, output]).to eq [input, expected]
          end
        end
      end
      

      【讨论】:

        【解决方案7】:

        您可以使用正则表达式来做到这一点,因为 ruby​​/rails 有精度限制。

        --先把数字转成字符串再做下面-

        输入 = "114.99999999999999999999"

        输入[/\d+.\d/]

        114.99

        【讨论】:

          【解决方案8】:

          我看到更“计算”的方式不在答案中。您可以考虑使用下面的方法。

          这也适用于其他编程语言,如 C/Java/Python 等(但转换语法会有所不同)。

          q = 1.0155555555555555
          (q * 1000).to_i / 1000.0
          => 1.015
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-01-29
            • 1970-01-01
            • 1970-01-01
            • 2011-03-09
            • 1970-01-01
            • 2011-05-10
            • 1970-01-01
            相关资源
            最近更新 更多