Rust与Go大整数除法结果差异原因及对齐方法问询
大整数除法:Rust与Go的取整差异及对齐方案
问题重现
当处理负数被除数的大整数除法时,Rust(使用num_bigint::BigInt)和Go(使用math/big.Int)的运算结果存在明显差异:
Rust 测试代码
use num_bigint::BigInt; use std::str::FromStr; fn main() { let n = BigInt::from_str("-4068122527548036313624952687320999183219209139149389824425650628448701979991259532638609685663741905551578447595775644964695361846038949186131821248175179900695219937607954907860922507949244416").unwrap(); let d = BigInt::from_str( "115792089237316195423570985008687907852837564279074904382605163141518161494337", ).unwrap(); let res = &n / &d; println!("res:{}", res); }
Go 测试代码
package main import "math/big" func main() { var n, d, res big.Int n.SetString("-4068122527548036313624952687320999183219209139149389824425650628448701979991259532638609685663741905551578447595775644964695361846038949186131821248175179900695219937607954907860922507949244416", 10) d.SetString("115792089237316195423570985008687907852837564279074904382605163141518161494337", 10) res.Div(&n, &d) println("res:", res.String()) }
输出结果
- Rust:
-35132991850681684771865987637349505035135798334672277824131793024179346079627919418184483600280933255791109974931548(向零取整) - Go:
-35132991850681684771865987637349505035135798334672277824131793024179346079627919418184483600280933255791109974931549(向负无穷取整)
差异原因
两种语言的大整数除法采用了不同的取整规则,这是语言设计阶段的明确决策:
- Rust:
num_bigint::BigInt的除法遵循向零取整(截断除法),直接舍弃小数部分,结果符号与被除数一致,和C、Python 2.x的整数除法行为对齐。 - Go:
math/big.Int.Div遵循向负无穷取整(地板除法),结果是小于等于真实商的最大整数,余数符号始终与除数一致,和Python 3+、Haskell等语言的规则一致,能保证a = (a/b)*b + a%b恒成立。
对齐方案
可以通过手动调整逻辑,让两种语言的除法行为统一到任意一种规则下:
方案1:统一为向负无穷取整(对齐Go的行为)
在Rust中,使用num_integer crate提供的div_floor方法,直接实现向负无穷取整的除法:
use num_bigint::BigInt; use num_integer::Integer; use std::str::FromStr; fn main() { let n = BigInt::from_str("-4068122527548036313624952687320999183219209139149389824425650628448701979991259532638609685663741905551578447595775644964695361846038949186131821248175179900695219937607954907860922507949244416").unwrap(); let d = BigInt::from_str("115792089237316195423570985008687907852837564279074904382605163141518161494337").unwrap(); let res = n.div_floor(&d); println!("res:{}", res); }
方案2:统一为向零取整(对齐Rust的行为)
在Go中,手动调整Div的结果:当被除数和除数符号不同且余数不为0时,将结果加1抵消地板除法的向下取整:
package main import "math/big" // divTowardsZero 实现向零取整的大整数除法 func divTowardsZero(n, d *big.Int) *big.Int { res := new(big.Int).Div(n, d) // 仅当被除数与除数符号不同,且余数不为0时调整结果 if n.Sign() != d.Sign() { rem := new(big.Int).Rem(n, d) if rem.Sign() != 0 { res.Add(res, big.NewInt(1)) } } return res } func main() { var n, d big.Int n.SetString("-4068122527548036313624952687320999183219209139149389824425650628448701979991259532638609685663741905551578447595775644964695361846038949186131821248175179900695219937607954907860922507949244416", 10) d.SetString("115792089237316195423570985008687907852837564279074904382605163141518161494337", 10) res := divTowardsZero(&n, &d) println("res:", res.String()) }
内容的提问来源于stack exchange,提问作者user76333
相关产品推荐
相关产品推荐

