Ric*_*ast 5 integer-overflow rust integer-arithmetic modular-arithmetic
我正在尝试为Rust u32和u64数据类型实现快速素性测试.作为它的一部分,我需要计算(n*n)%d其中n和d是u32(或u64,分别地).
虽然结果很容易适合数据类型,但我对如何计算它感到茫然.据我所知,没有处理器原语.
因为u32我们可以伪造它 - 铸造u64,以便产品不会溢出,然后取模数,然后再回落u32,知道这不会溢出.但是因为我没有u128数据类型(据我所知),这个技巧不起作用u64.
因此,对u64,我能想到的最明显的方式来完成,这是莫名其妙地计算x*y得到一对(carry, product)的u64,所以我们捕获,而不是仅仅失去它(或恐慌,或其他)溢出量.
有没有办法做到这一点?还是另一种解决问题的标准方法?
Richard Rast指出维基百科版本仅适用于 63 位整数。我扩展了 Boiethios 提供的代码以处理全范围的 64 位无符号整数。
fn mul_mod64(mut x: u64, mut y: u64, m: u64) -> u64 {
let msb = 0x8000_0000_0000_0000;
let mut d = 0;
let mp2 = m >> 1;
x %= m;
y %= m;
if m & msb == 0 {
for _ in 0..64 {
d = if d > mp2 {
(d << 1) - m
} else {
d << 1
};
if x & msb != 0 {
d += y;
}
if d >= m {
d -= m;
}
x <<= 1;
}
d
} else {
for _ in 0..64 {
d = if d > mp2 {
d.wrapping_shl(1).wrapping_sub(m)
} else {
// the case d == m && x == 0 is taken care of
// after the end of the loop
d << 1
};
if x & msb != 0 {
let (mut d1, overflow) = d.overflowing_add(y);
if overflow {
d1 = d1.wrapping_sub(m);
}
d = if d1 >= m { d1 - m } else { d1 };
}
x <<= 1;
}
if d >= m { d - m } else { d }
}
}
#[test]
fn test_mul_mod64() {
let half = 1 << 16;
let max = std::u64::MAX;
assert_eq!(mul_mod64(0, 0, 2), 0);
assert_eq!(mul_mod64(1, 0, 2), 0);
assert_eq!(mul_mod64(0, 1, 2), 0);
assert_eq!(mul_mod64(1, 1, 2), 1);
assert_eq!(mul_mod64(42, 1, 2), 0);
assert_eq!(mul_mod64(1, 42, 2), 0);
assert_eq!(mul_mod64(42, 42, 2), 0);
assert_eq!(mul_mod64(42, 42, 42), 0);
assert_eq!(mul_mod64(42, 42, 41), 1);
assert_eq!(mul_mod64(1239876, 2948635, 234897), 163320);
assert_eq!(mul_mod64(1239876, 2948635, half), 18476);
assert_eq!(mul_mod64(half, half, half), 0);
assert_eq!(mul_mod64(half+1, half+1, half), 1);
assert_eq!(mul_mod64(max, max, max), 0);
assert_eq!(mul_mod64(1239876, 2948635, max), 3655941769260);
assert_eq!(mul_mod64(1239876, max, max), 0);
assert_eq!(mul_mod64(1239876, max-1, max), max-1239876);
assert_eq!(mul_mod64(max, 2948635, max), 0);
assert_eq!(mul_mod64(max-1, 2948635, max), max-2948635);
assert_eq!(mul_mod64(max-1, max-1, max), 1);
assert_eq!(mul_mod64(2, max/2, max-1), 0);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
607 次 |
| 最近记录: |