C#,Modulo操作比计算器产生不同的结果

时间:2018-03-23 18:21:05

标签: c# math mod

所以我想写这个方法:142 ^ 23(mod 187),并使用任何计算器我得到结果65但是这段代码: double number = Math.Pow(142, 23) % 187 我得到了53的结果。为什么这样,我在这里做错了什么?

2 个答案:

答案 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的分数分量。自从我这么做以来已经很长时间了,所以我将它作为练习留给读者:)