floor(a,b)和a // b的结果不同

时间:2018-07-22 00:14:37

标签: lua floating-accuracy

--this function isn't relevant, but I included it for completeness
function gcd(a, b)
    local temp
    while b > 0 do
        temp = a % b
        a = b
        b = temp
    end
    return a
end

function extendedgcd(a, b)
    if b == 0 then
        return 1, 0, a
    end
    local x, y, d = extendedgcd(b, a % b)
    --this assertion fails for a = 568784642688024576, b = 5
    --left side gives 113756928537604915 (correct), while right side gives 113756928537604912 (wrong)
    assert(a // b == math.floor(a / b))
    --so here, I can't use math.floor
    return y, x - a // b * y, d
end

function modularinverse(e, mod)
    if gcd(e, mod) > 1 then
        return nil
    end
    local x, _, _ = extendedgcd(e, mod)
    if x < 0 then
        x = x % mod
    end
    if x * e % mod ~= 1 then
        error(string.format("Modular inverse (%d) doesn't produce 1 (e = %d, mod = %d)", x, e, mod))
    end
    return x
end

modularinverse(5, 568784642688024576)

对于a = 5b = 568784642688024576extendedgcd中的断言失败。我不是FP精度方面的专家,但是两者之间相差3,因此我认为这里不存在舍入/精度误差。但是我可能错了。

通常我只使用//,但是我不能,因为目标平台没有运行Lua 5.3,这是在添加操作符时开始的。

我想念什么?我如何使其与floor一起使用,或者还有另一种方法?

还要注意:当我用Python重新编写它(用math.floor//)时,发生了相同的问题。

2 个答案:

答案 0 :(得分:3)

更精确的答案:

纸和铅笔将显示: 568784642688024576 / 5 = 113756928537604915.02

该商最精确的二进制表示形式是双精度数字:

0100001101111001010000100101010100101110010000111001001100110011

以小数表示的是:1.13756928537604912E17(请注意... 912结尾)

现在,如果将二进制表示形式减一,就像这样:

0100001101111001010000100101010100101110010000111001001100110010

那么它等于:1.13756928537604896E17(最后是... 896!)

如果要原始二进制数加1,如下所示:

0100001101111001010000100101010100101110010000111001001100110100

然后等于:1.13756928537604928E17(末尾... 928!)

因此,这些数字具有精确的双精度二进制表示形式:

113756928537604896

113756928537604912(最接近实际商)

113756928537604928

可以使用在线转换器(例如here)验证以上内容。

因此,本课是:

整数除法将给出正确的答案(即商的整数部分)。地板浮点除法依赖于数字的二进制表示,这并不总是精确的。

超出了此答案的范围,但需要阅读以真的了解以下任何内容:

上面的二进制数字如何表示它们的十进制等效项?

  • 简短答案:IEEE-754。如果您计划研究该标准文档,希望您能过得愉快。

///之间的算法区别是什么?

  • 换句话说,//为什么能给我们上面想要的答案?

答案 1 :(得分:-1)

卢阿的数字是双精度的,对不对?一旦达到2 ^ 52以上,整数表示中的差距就会越来越大。

568784642688024576大于2 ^ 58,因此您可能会遇到一些缺口。如果是这样,那么//看起来就好像正确地弥补了差距,而底线可能就没有。

如果代码要处理接近2 ^ 64的整数值很重要,那么可能值得寻找一个插件或可以让您使用64位整数的工具。或者,如果您需要处理更大的数字,则可能有一个库或一些用于处理非常大的数字的东西。