PHP使用fmod()与pow()计算模幂返回值错误问题求助
问题原因
你所有错误结果的来源非常明确,和你推测的浮点数精度问题部分相关,但还有运算符认知错误的问题:
pow(20,37)%77返回0:绝大多数语言标准库自带的双参数pow是浮点运算函数,2037是一个49位的十进制整数,远超过双精度浮点数可以精确存储的整数上限(253约为16位十进制数),计算出的幂结果本身已经因为精度丢失完全失真,取模自然得到错误值,极端情况下甚至会因为数值溢出返回无穷大,和整数取模后得到0。fmod(pow(20,37), 77)返回33:fmod只是适配浮点数的取模函数,并没有解决底层pow返回值精度丢失的问题,结果错误是必然的。20^37%77返回49:在绝大多数通用编程语言里,^不是幂运算符,而是按位异或位运算符,你实际执行的运算逻辑是「20和37做按位异或,结果再对77取模」,得到的49和正确答案48接近完全是巧合,和幂运算没有任何关系。
你提到JS和C#能得到正确结果,本质是这两个场景下你大概率用了对的接口:C#的BigInteger.ModPow、JS的BigInt类型大整数运算,都避开了浮点数精度和运算符用错的问题。
正确实现方案
公私钥加密里用到的m^e mod n是典型的模幂运算,绝对不要先算出完整的m^e再取模——这个值会随着指数e增大快速膨胀到远超普通基础类型的存储上限,正确做法是直接使用原生支持的模幂接口,这类接口内部会用快速幂逻辑边做乘法边取模,既不会溢出,也不会有精度损失,运算效率还高:
- Python直接用内置三参数pow:
pow(20, 37, 77),执行后直接返回正确结果48。 - C/C++不要调用math.h的浮点pow,要么自己实现整数快速幂逻辑(注意乘法步骤的溢出判断,必要时用128位整数或者大数库),要么用大数库提供的模幂接口。
- JavaScript用BigInt类型做运算避免精度丢失:
(20n ** 37n) % 77n,即可得到正确结果48,大指数场景建议自己实现快速幂优化性能。 - C#直接调用标准库的
BigInteger.ModPow(20, 37, 77)即可,这也是你之前能得到正确结果的正确写法。
排查方向
如果后续自己实现模幂逻辑出问题,优先检查两点:
- 所有运算链路有没有用到浮点数类型、浮点运算函数,只要出现浮点数参与大整数模运算,精度丢失是必然的。
- 有没有误用运算符,比如把按位异或
^当成幂运算符使用,这个是新手写这类逻辑非常容易踩的坑。
内容的提问来源于stack exchange,提问作者agronxyz
相关产品推荐
相关产品推荐

