截取浮点数而不进行四舍五入

30

我有一个浮点数,想将其截断为三个小数位,但不想进行四舍五入。

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

我该如何在Ruby中实现此功能?


2
负数会怎样处理,例如:-1.01555? - Mark Byers
@MarkByers 对于我的操作,这个数字永远不会是负数。 - ab217
8个回答

25

您也可以将其转换为BigDecimal,然后对其进行截断。

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

谢谢,Sid。这正是我在寻找的方法。 - Elliot Larson
干杯 @ElliotLarson! - Sid Krishnan
2
它在 59.99999999999999999999.to_d.truncate(2).to_f 失败,返回 60.0!! - Anwar
@Anwar 这是因为精度对于普通的Float来说太细了。即使是59.99999999999999999999.inspect也会被转化为"60.0"。因此,在to_d之前进行了转换。 - Alex Fortuna
嘿,伙计们,当整数部分足够大时,存在精度问题。111111111.9999999.to_d.truncate(2).to_f111111112.0。请看下面我修改过的版本。干杯! - Alex Fortuna
@Anwar 我建议阅读:计算机处理器的浮点运算https://en.wikipedia.org/wiki/Floating-point_arithmetic - Dennis

22

假设你有一个 float,尝试这样做:

(x * 1000).floor / 1000.0

结果:

1.015

在线查看实现效果:ideone


1
不是所有情况下都适用,例如 (128.653 * 1000) / 1000.0 => 128.652 # 不起作用 - 因为乘出的值变成了 128652.999999。 - Chris Kimpton
这不是一个答案,因为它依赖于向下取整,这没有任何意义。 - Dennis

18

自从 ruby 2.4 版本以来,Float#truncate 方法接受一个可选参数表示要保留的小数位数:

1.0155555555555555.truncate(3)
# => 1.015

7
乘以一千,向下取整,除以一千,确保执行浮点数除法。
(x * 1000).floor / 1000.0

在Ruby 1.9.2中,可以使用之前版本中不可用的round版本。
(x - 0.0005).round(3)

你不是很喜欢double类型吗... pry(main)> (1.0155555 * 1000).floor / 1000.0 => 1.015 # 运行正常 pry(main)> (128.653 * 1000).floor / 1000.0 => 128.652 # 不正常 - 因为乘积结果为128652.999999 - Chris Kimpton

3

sid的回答很好,但它错过了第一个要求,因此未能通过Anwar的测试。该要求是我们必须从原始状态开始,以便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

现在分享这个问题,因为我今天刚遇到这个问题 : )


0

这个解决方案基于@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

0

我发现更“计算机化”的方法并不在答案中。您可以考虑使用以下方法。

这种方法在其他编程语言中也适用,如C / Java / Python等(但强制转换语法可能会有所不同)。

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

-1

你可以使用正则表达式来实现,因为 Ruby/Rails 有精度限制。

-- 首先将数字转换为字符串,然后执行以下操作 --

input = "114.99999999999999999999"

input[/\d+.\d/]

114.99


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接