Lua:舍入数字然后截断

use*_*552 18 lua truncate rounding

哪个是最有效的方法来舍入一个数字然后截断它(在四舍五入后删除小数位)?

例如,如果小数大于0.5(即0.6,0.7等),我想向上舍入然后截断(情况1).否则,我想截断(案例2)

for example:
232.98266601563 => after rounding and truncate = 233 (case 1)
232.49445450000 => after rounding and truncate = 232 (case 2)
232.50000000000 => after rounding and truncate = 232 (case 2)
Run Code Online (Sandbox Code Playgroud)

Ola*_*a M 25

Lua中没有内置的math.round()函数,但您可以执行以下操作: print(math.floor(a+0.5)).

  • 对于 a = 0.49999999999999994,返回 1;它应该返回 0。 (2认同)

RBe*_*eig 11

对于除整数之外的十进制数字舍入有用的技巧是通过格式化的ASCII文本传递值,并使用%f格式字符串指定所需的舍入.例如

mils = tonumber(string.format("%.3f", exact))
Run Code Online (Sandbox Code Playgroud)

将任意值四舍五入exact为0.001的倍数.

在使用math.floor()or 之前和之后使用缩放可以得到类似的结果math.ceil(),但是根据您对边缘情况处理的期望得到正确的细节可能是棘手的.并不是说这不是一个问题string.format(),但是已经做了很多工作来使它产生"预期的"结果.

舍入到除10的幂之外的其他数字仍然需要缩放,并且仍然具有所有棘手的边缘情况.一种易于表达且具有稳定行为的方法是编写

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

并调整frac(和可能的符号exact)的确切条件,以获得您想要的边缘情况.

  • 不幸的是,根据所使用的 Lua 版本,“string.format()”的舍入方式有所不同。请参阅[我关于 LuaJIT 的问题](/sf/ask/4519344231/),当遇到“精确”的一半时,它更喜欢向上舍入,而大多数其他人则向下舍入。 (2认同)

Ped*_*eno 9

如果您的 Lua 像大多数一样使用双精度 IEC-559(又名 IEEE-754)浮点数,并且您的数字相对较小(该方法保证适用于 -2 51和 2 51之间的输入),则以下有效代码将使用 FPU 的当前舍入模式执行舍入,通常舍入到最近,与偶数相关联:

local function round(num)
  return num + (2^52 + 2^51) - (2^52 + 2^51)
end
Run Code Online (Sandbox Code Playgroud)

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

例如,当 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)
-- Most rounding algorithms you find on the net, including Ola M's answer,
-- fail this one:
test(0.49999999999999994, 0)
-- Ties are rounded to the nearest even number, rather than always up:
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")
Run Code Online (Sandbox Code Playgroud)

这是另一种(当然效率较低)算法,它执行相同的 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
Run Code Online (Sandbox Code Playgroud)

  • @RolandIllig 我的测试套件不是很详尽;它不会检查所有极端情况。您的数字失败,例如 2.75,四舍五入为 2.0。对 0 到 2^52-1 之间的任何数字加减 2^52,将其推入范围 2^52..2^53,在调整回来之前,双精度浮点数会丢失所有小数位。为了覆盖负数,我添加了 2^51 以确保中间结果仍在 2^52..2^53 范围内。如果你将它推到超过 2^53(就像你的数字经常做的那样),你也会失去偶数;例如,2^53+1 不能用双精度精确表示。 (2认同)
  • “你也失去了偶数” - 我的意思是你失去了*奇数*数字。 (2认同)

小智 8

要同时支持负数,请使用:

function round(x)
  return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
Run Code Online (Sandbox Code Playgroud)

  • 对于 x = 0.499999999999999994,返回 1;对于 -0.49999999999999994,返回 -1;它应该返回 0。请参阅我的答案,了解正确舍入到最接近或偶数的版本。 (3认同)

小智 5

对于不良舍入(切掉末端):

function round(number)
  return number - (number % 1)
end
Run Code Online (Sandbox Code Playgroud)

好吧,如果您愿意,您可以扩展它以实现良好的舍入。

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))
Run Code Online (Sandbox Code Playgroud)

预期成绩:

3, 3, 42, 5,5


小智 5

这是一个四舍五入到任意位数的数字(如果未定义,则为 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
Run Code Online (Sandbox Code Playgroud)