如何解决可能的乘法溢出以获得正确的模数运算?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何解决可能的乘法溢出以获得正确的模数运算?相关的知识,希望对你有一定的参考价值。

我必须执行(a * b) % m,但abm是128位无符号类型,并且在乘法期间溢出是一个很大的可能性。我怎样才能得到正确的答案(可能更多地使用%)?

我正在尝试在Rust中实现模块化指数函数,其中最大的内置类型是u128(这是我可以使用的最大值)。所有三个变量都非常大,所以(a * b) > 2^128很容易。我可以使用a.overflowing_mul(b)来检测是否发生溢出,但我不知道如何从溢出的结果(可以被认为是(a * b) % 2^128)返回以获得(a * b) % m

我的模块化指数代码如下所示(目前没有添加溢出支持):

fn mod_exp(b: u128, e: u128, m: u128) {
    (0..e).fold(1, |x, _| (x * b) % m)
    //                    ^^^^^^^^^^^
}

从数学角度来看:

(a * b) % m IS ACTUALLY (a * b) % B % m
| B = current base (2^128)

例子:

// Mathematical
(9 * 13) % 11 = 7
// Real (base 20):
(9 * 13) % (B = 20) % 11 = 6
         ^^^^^^^^^^        ^ should be 7

(8 * 4) % 14 = 4
(8 * 4) % (B = 16) % 14 = 0
        ^^^^^^^^^^        ^ should be 4
答案

这种实现基于将128位产品分成四个64位产品,速度是num_bigint::BigUint的五倍,是uint::U256的十倍,是gmp::mpz::Mpz的2.3倍:

fn mul_mod(a: u128, b: u128, m: u128) -> u128 {
    if m <= 1 << 64 {
        ((a % m) * (b % m)) % m
    } else {
        let add = |x: u128, y: u128| x.checked_sub(m - y).unwrap_or_else(|| x + y);
        let split = |x: u128| (x >> 64, x & !(!0 << 64));
        let (a_hi, a_lo) = split(a);
        let (b_hi, b_lo) = split(b);
        let mut c = a_hi * b_hi % m;
        let (d_hi, d_lo) = split(a_lo * b_hi);
        c = add(c, d_hi);
        let (e_hi, e_lo) = split(a_hi * b_lo);
        c = add(c, e_hi);
        for _ in 0..64 {
            c = add(c, c);
        }
        c = add(c, d_lo);
        c = add(c, e_lo);
        let (f_hi, f_lo) = split(a_lo * b_lo);
        c = add(c, f_hi);
        for _ in 0..64 {
            c = add(c, c);
        }
        add(c, f_lo)
    }
}

(警告:这些实现中没有一个适用于加密代码,因为它们不会对侧通道攻击加强。)

以上是关于如何解决可能的乘法溢出以获得正确的模数运算?的主要内容,如果未能解决你的问题,请参考以下文章

我如何获得负股息的正模数

快速幂乘法&快速幂取余

考前自学系列·计算机组成原理·补码定点加减运算和溢出判断,浮点数的加减运算,原码的乘法

大数乘法取模运算(二进制)

为啥模数运算符很慢?

[位运算] 64位整数乘法(mod 一个数)