Unsigned Long Int 在计算 pow 时溢出

Unsigned Long Int overflow when calculating pow

我正在尝试制作一个可以快速计算 x^y mod z 的函数。它在计算 2^63 mod 3 之类的东西时效果很好,但在 2^64 mod 3 和更高的指数时它只是 returns 0。

我怀疑某处溢出,但无法确定。我在进行计算 (* 和 mod) 的地方尝试了显式转换,我还制作了我的存储变量 (resPowcurPow) unsigned long long int(如建议的那样here) 但这并没有多大帮助。

typedef unsigned long int lint;

lint fastpow(lint nBase, lint nExp, lint nMod) { 
    int lastTrueBit = 0;
    unsigned long long int resPow = 1ULL;

    unsigned long long int curPow = nBase;
    for (int i = 0; i < 32; i++) {
        int currentBit = getBit(nExp, i);

        if (currentBit == 1) {
            for (lint j = 0; j < i - lastTrueBit; j++) {
                curPow = curPow * curPow;
            }
            resPow =resPow * curPow;
            lastTrueBit = i;
        }
    }
    return resPow % nMod;
}

I am suspecting an overflow somewhere,

是的,curPow * curPowresPow * curPow 都可能在数学上溢出。

这里遏制溢出的常用方法是对中间产品执行mod

        // curPow = curPow * curPow;
        curPow = (curPow * curPow) % nMod;
    // resPow =resPow * curPow;
    resPow = (resPow * curPow) % nMod;

nMod < ULLONG_MAX/(nMod - 1)时,这就足够了。 (mod 值是 unsigned long long 精度的一半)。否则需要采取更极端的措施,如:Modular exponentiation without range restriction.


小东西

for(int i = 0; i < 32; i++) 假定 lint/unsigned long 是 32 位。可移植代码将避免幻数unsigned long 在各种平台上是 64 位。

这里不需要

LLU 仍然有助于消除各种编译器警告。

// unsigned long long int resPow = 1ULL;
unsigned long long int resPow = 1U;