Lua:对数字进行四舍五入,然后截断。

什么是最有效的四舍五入并截断(在进行四舍五入后删除小数位)的方法?

例如,如果小数超过0.5(即0.6、0.7等),我想要进行四舍五入并截断(情况1)。否则,我想要进行截断(情况2)。

例如:
232.98266601563 => 四舍五入并截断后 = 233(情况1)
232.49445450000 => 四舍五入并截断后 = 232(情况2)
232.50000000000 => 四舍五入并截断后 = 232(情况2)
点赞
用户1646802
用户1646802

在 Lua 中没有内置的 math.round() 函数,但是你可以使用以下语句:print(math.floor(a+0.5))

2013-08-19 12:10:40
用户1847592
用户1847592

应该使用 math.ceil(a-0.5) 来正确处理半整数。

2013-08-19 20:18:39
用户68204
用户68204

一个有用的小技巧,在除整数以外的小数位上取整是传递该值通过格式化ASCII文本,并使用%f格式串来指定所需的舍入。例如:

mils = tonumber(string.format("%.3f", exact))

将会将exact中的任意值舍入为0.001的倍数。

类似的结果可以在使用math.floor()math.ceil()之前和之后进行缩放时获得,但根据您对边缘情况的期望得到正确的细节可能会很棘手。不是说string.format()不涉及到这个问题,而是说它在产生“预期”结果方面花费了很多精力。

将某个数舍入为除十的倍数仍需要缩放,并且还有所有棘手的边缘情况。一种表达简单且行为稳定的方法是编写:

function round(exact, quantum)
    local quant,frac = math.modf(exact/quantum)
    return quantum * (quant + (frac > 0.5 and 1 or 0))
end

并调整关于frac的确切条件(以及可能的exact符号)来获取您想要的边缘情况。

2013-08-19 20:30:36
用户388498
用户388498

为了支持负数,可以使用以下代码:

function round(x)
  return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
2014-11-06 11:04:46
用户4856617
用户4856617

针对不好的四舍五入(舍去小数点后的数):

function round(number)
  return number - (number % 1)
end

嗯,如果你想,你可以将此扩展为好的四舍五入。

function round(number)
  if (number - (number % 0.1)) - (number - (number % 1)) < 0.5 then
    number = number - (number % 1)
  else
    number = (number - (number % 1)) + 1
  end
 return number
end

print(round(3.1))
print(round(math.pi))
print(round(42))
print(round(4.5))
print(round(4.6))

预期结果:

334255

2016-04-22 15:53:53
用户4425144
用户4425144

以下是将数字舍入到任意位数(如果没有定义则为0)的函数:

function round(x, n)
    n = math.pow(10, n or 0)
    x = x * n
    if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
    return x / n
end
2016-06-13 14:51:52
用户11866183
用户11866183

我喜欢 RBerteig 上面的回答:mils = tonumber(string.format("%.3f", exact))。 我将其扩展为函数调用并添加了精度值。

function round(number, precision)
   local fmtStr = string.format('%%0.%sf',precision)
   number = string.format(fmtStr,number)
   return number
end
2019-07-31 21:23:45
用户2428487
用户2428487

如果您的 Lua 使用 double precision IEC-559 (也称为 IEEE-754)的浮点数,大多数情况下,如果您的数字相对较小(该方法可保证适用于 -251 至 251 之间的输入),则以下高效的代码将使用您的 FPU 的当前舍入模式执行四舍五入,通常是最接近的,平局时选择偶数:

local function round(num)
  return num + (2^52 + 2^51) - (2^52 + 2^51)
end

(请注意,括号中的数字是在编译时计算的,它们不会影响运行时)。

例如,当 FPU 设置为最接近或偶数舍入时,此单元测试将打印“所有测试通过”:

local function testnum(num, expected)
  if round(num) ~= expected then
    error(("Failure rounding %.17g, expected %.17g, actual %.17g")
          :format(num+0, expected+0, round(num)+0))
  end
end

local function test(num, expected)
  testnum(num, expected)
  testnum(-num, -expected)
end

test(0, 0)
test(0.2, 0)
test(0.4, 0)
-- 大多数在网络上找到的舍入算法,包括 Ola M 的答案,
-- 都无法通过此测试:
test(0.49999999999999994, 0)
-- 平局时选择偶数进行舍入,而不是总是向上:
test(0.5, 0)
test(0.5000000000000001, 1)
test(1.4999999999999998, 1)
test(1.5, 2)
test(2.5, 2)
test(3.5, 4)
test(2^51-0.5, 2^51)
test(2^51-0.75, 2^51-1)
test(2^51-1.25, 2^51-1)
test(2^51-1.5, 2^51-2)
print("All tests passed")

下面是另一个(不那么高效的)算法,它执行相同的 FPU 舍入,但适用于所有数字:

local function round(num)
  local ofs = 2^52
  if math.abs(num) > ofs then
    return num
  end
  return num < 0 and num - ofs + ofs or num + ofs - ofs
end
2019-10-16 10:56:03
用户16184778
用户16184778

以下是一个灵活的函数,可以将数字四舍五入到不同的小数位数。我用负数、大数、小数以及各种边缘情况进行了测试,它非常有用且可靠:

function Round(num, dp)
    --[[
    将数字四舍五入到指定的小数位数,可以是负数,
    例如,-1 表示四舍五入到十位数,

    举例:
        173.2562 四舍五入为 0 位小数为 173.0
        173.2562 四舍五入为 2 位小数为 173.26
        173.2562 四舍五入为 -1 位小数为 170.0
    ]]--
    local mult = 10^(dp or 0)
    return math.floor(num * mult + 0.5)/mult
end
2021-06-10 08:40:32
用户2932052
用户2932052

对于保留给定小数位数(也可以为负数)的四舍五入,我建议采用如下解决方案,该方案结合了已有答案中的发现,特别是Pedro Gimeno提供的鼓舞人心的答案。 我测试了一些我感兴趣的边角案例,但不能声称这使得此函数100%可靠:

function round(number, decimals)
  local scale = 10^decimals
  local c = 2^52 + 2^51
  return ((number * scale + c ) - c) / scale
end

这些案例说明了四舍五入到最近偶数的属性(应该是大多数计算机的默认值):

assert(round(0.5, 0) == 0)
assert(round(-0.5, 0) == 0)
assert(round(1.5, 0) == 2)
assert(round(-1.5, 0) == -2)
assert(round(0.05, 1) == 0)
assert(round(-0.05, 1) == 0)
assert(round(0.15, 1) == 0.2)
assert(round(-0.15, 1) == -0.2)

我知道我的答案没有处理实际问题的第三种情况,但是为了符合IEEE-754标准,我的方法是有意义的。 因此,我期望结果取决于FPU中设置的当前舍入模式,其中FE_TONEAREST是默认值。 这就是为什么似乎很有可能在设置FE_TOWARDZERO之后(但是您可以在Lua中这样做)该解决方案将精确地返回问题中要求的结果。

2021-06-14 19:18:29
用户14335962
用户14335962

尝试使用math.ceil(number + 0.5),这是根据这个 Wikipedia 页面。如果我没错的话,这只是对正整数进行四舍五入。如果是负数,需要进行math.floor(number - 0.5)

2021-08-27 05:16:37
用户14672114
用户14672114

如果有用的话,我已经用哈希值生成了一个通用版本的 LUA 逻辑,但这次是针对 truncate()

** 强调的文本预先道歉我不熟悉 lua 语法,所以这是在 AWK/lua 混合中,但希望这应该是直观的

-- 由于 lua-magic 已经在 2^(52-to-53) 区域内,因此必须使用
-- 比真正的 IEEE754 双倍机器 epsilon 更粗粒度的 delta
-- 2^-52。
function trunc_lua(x,s) {
    return \
      ((x*(s=(-1)^(x<-x)) \
           - 2^-1 + 2^-50 \  -- 也可以写成 2^-50-5^0/2
           -  _LUAMAGIC  \   -- 如果你喜欢对称的代码,就这样
                         \   --
           +  _LUAMAGIC \
      )  *(s) };

本质上与四舍五入相同,但强制将所有输入处理在正值区域,并带有 -1*(0.5-delta) 偏移量。最小的 delta 值我能够达到是 2^-52 ~ 2.222e-16

Lua-magic 值必须在所有这些预处理步骤之后出现,否则可能会发生精度丢失。最后,恢复原始输入的符号。

这两个 "乘法" 只是低成本的符号翻转。对于最初的负值,符号翻转 4 次(2 次手动翻转和往返于尾数末尾),而任何 x >= 0,包括 -0.0 的值,只翻转两次。避免了所有三元函数调用、浮点除法和整数模除,只有一个检查 x<0 的条件。

使用说明:

  • (1)不检查输入的有效性或恶意负载。
  • (2)不使用快速检查零。
  • (3)不检查可能使此逻辑无意义的极限输入。
  • (4)不尝试美化值的格式。
2021-08-28 20:53:09
用户2323297
用户2323297

如果 math.round 不存在,则:

function math.round(x, n)
    return tonumber(string.format("%." .. n .. "f", x))
end
2022-05-17 18:49:58