所以我想写这个方法:142 ^ 23(mod 187),并使用任何计算器我得到结果65但是这段代码:
double number = Math.Pow(142, 23) % 187
我得到了53的结果。为什么这样,我在这里做错了什么?
答案 0 :(得分:11)
Math.Pow(142, 23)
太大而无法准确地用双精度表示。所以你的模数是通过有损计算完成的。
这将给出正确答案:
BigInteger.ModPow(142, 23, 187);
BigInteger
可以在System.Numerics
命名空间和程序集中找到。
如果你想要在你的问题中使用的大小整数,你也可以自己有效地实现它。
private static int ModPow(int basenum, int exponent, int modulus)
{
if (modulus == 1)
{
return 0;
}
int result = 1;
for (var i = 0; i < exponent; i++)
{
result = (result * basenum) % modulus;
}
return result;
}
BigInteger
使用二进制求幂更聪明,这对于真正庞大的数字会更好。
答案 1 :(得分:2)
如果我们使用BigInteger
来计算指数的完整结果:
var bi = BigInteger.Pow(142, 23);
Debug.WriteLine(bi);
我们得到了这么大的数字:
31814999504641997296916177121902819369397243609088
or
3.1814999504642E+49
如果我们然后将该值转换为double,导致精度损失,然后返回BigInteger
:
var d = (double) bi;
bi = new BigInteger(d);
Debug.WriteLine(bi);
我们得到:
31814999504641997296916177121902819369397243609088 -- BigInteger
31814999504641993108158684988768059669621048868864 -- BigInteger -> double -> BigInteger
^ oh no mah precision
在十六进制中,精度损失更明显:
15C4 C9EB 18CD 25CE 858D 6C2D C3E5 D319 BC9B 8000 00
15C4 C9EB 18CD 2500 0000 0000 0000 0000 0000 0000 00
^ oh no mah precision
您会注意到精度损失发生在第17个十进制数字或第14个十六进制数字。
为什么?
A double
is stored using IEEE-754 encoding:
Significand or mantissa: 0-51
Exponent: 52-62
Sign (0 = Positive, 1 = Negative) 63
这里的关键是尾数为52位。我们的14个十六进制数字是56位,接近52位限制。我们如何解释4位差异?
(我认为我在接下来的解释中犯了一个错误。如果有人可以指出,我将不胜感激)
最后一个未更改的十六进制数字为C
,或二进制为1100
;由于最后两位是零,我们的数字编码为54位,而不是56位。因此,它实际上是2位差异。
我们如何解释最后两位?这是由于如何确定IEEE-754的分数分量。自从我这么做以来已经很长时间了,所以我将它作为练习留给读者:)