C# 对于C中的不同.NET版本,模运算符(%)给出了不同的结果#

C# 对于C中的不同.NET版本,模运算符(%)给出了不同的结果#,c#,.net,modulo,C#,.net,Modulo,我正在加密用户的输入以生成密码字符串。但是一行代码在不同版本的框架中给出了不同的结果。用户按下的键值的部分代码: 按键:1。变量ascii为49。经过计算后的‘e’和‘n’值: e = 103, n = 143, Math.Pow(ascii, e) % n 上述代码的结果: 在.NET3.5中(C#) 给出9.0 在.NET4中(C#) 给出77.0 Math.Pow()在两个版本中都给出了正确(相同)的结果 原因是什么?是否有解决方案?浮点精度可能因机器而异,以及 然而,.NET为

我正在加密用户的输入以生成密码字符串。但是一行代码在不同版本的框架中给出了不同的结果。用户按下的键值的部分代码:

按键:1。变量
ascii
为49。经过计算后的‘e’和‘n’值:

e = 103, 
n = 143,

Math.Pow(ascii, e) % n
上述代码的结果:

  • 在.NET3.5中(C#)

    给出
    9.0

  • 在.NET4中(C#)

    给出
    77.0

Math.Pow()
在两个版本中都给出了正确(相同)的结果


原因是什么?是否有解决方案?

浮点精度可能因机器而异,以及

然而,.NET为你的应用程序创建了一个虚拟机。。。但是版本之间有变化

因此,你不应该依赖它来产生一致的结果。对于加密,使用框架提供的类,而不是滚动自己的类。

适用于双精度浮点数;因此,您不应期望结果的准确性超过预期:

所有浮点数的有效位数也有限,这也决定了浮点数逼近实数的精度。
Double
值的精度最高为15位小数,但内部最多保留17位小数

然而,模运算要求所有数字都是精确的。在您的例子中,您正在计算49103,其结果由175位数字组成,这使得模运算在您的两个答案中都没有意义

要计算出正确的值,应该使用类(在.NET4.0中引入)提供的任意精度算法

编辑:正如Mark Peters在下面的评论中指出的,您应该使用专门用于此类操作的方法:

int val = (int)BigInteger.ModPow(49, 103, 143);   // gives 114

你们看到的是双精度的舍入误差
Math.Pow
与double一起工作,区别如下:

.NET2.0和3.5=>
var powerResult=Math.Pow(ascii,e)返回:

1.2308248131348429E+174
1.2308248131348427E+174
.NET4.0和4.5=>
var powerResult=Math.Pow(ascii,e)返回:

1.2308248131348429E+174
1.2308248131348427E+174

注意
E
之前的最后一位数字,这会导致结果的差异它不是模运算符
(%)

也许最好自己用整数算法来计算它。比如:

int n = 143;
int e = 103;
int result = 1;
int ascii = (int) 'a';

for (i = 0; i < e; ++i) 
    result = result * ascii % n;
int n=143;
int e=103;
int结果=1;
int ascii=(int)“a”;
对于(i=0;i

您可以将性能与其他答案中发布的BigInteger解决方案的性能进行比较。

除了您的哈希函数不是一个很好的函数之外*,代码最大的问题不是它根据.NET的版本返回不同的数字,但在这两种情况下,它都返回一个完全没有意义的数字:问题的正确答案是

49103模块143=是114。()

您可以使用以下代码计算此答案:

private static int PowMod(int a, int b, int mod) {
    if (b == 0) {
        return 1;
    }
    var tmp = PowMod(a, b/2, mod);
    tmp *= tmp;
    if (b%2 != 0) {
        tmp *= a;
    }
    return tmp%mod;
}
您的计算产生不同结果的原因是为了产生答案,您使用了一个中间值,该值会删除49103数字的大部分有效数字:它的175个数字中只有前16个是正确的

1230824813134842807283798520430636310264067713738977819859474030746648511411697029659004340261471771152928833391663821316264359104254030819694748088798262075483562075061997649
其余159位数字都错了。然而,mod操作寻求的结果要求每个数字都是正确的,包括最后一个数字。因此,即使对.NET 4中可能实现的
Math.Pow
的精度进行了最微小的改进,也会导致计算结果的巨大差异,这实际上会产生任意结果


*由于这个问题涉及在密码散列的情况下将整数提高到高倍,因此在决定是否应将当前方法更改为可能更好的方法之前,阅读可能是一个非常好的主意。

关于代码的糟糕方式,有很多答案。然而,至于为什么结果不同

英特尔内部使用80位格式,以获得更高的中间结果精度。因此,如果一个值在处理器寄存器中,它将获得80位,但当它写入堆栈时,它将存储在64位

我希望较新版本的.NET在其即时(JIT)编译中具有更好的优化器,因此它将值保留在寄存器中,而不是将其写入堆栈,然后从堆栈中读回

JIT现在可能可以在寄存器中而不是堆栈中返回值。或将值传递给寄存器中的MOD函数

另请参见堆栈溢出问题


其他处理器(例如ARM)将对此代码给出不同的结果。

当然,问题中的两个答案都是错误的。事实上,你似乎并不关心这一点,这是令人担忧的。你需要返回几个步骤。“我正在加密用户的输入以生成密码字符串”这一部分已经可疑。你到底想做什么?您想以加密或哈希形式存储密码吗?你想用它作为熵来生成一个随机值吗?您的安全目标是什么?虽然这个问题说明了浮点运算的一个有趣问题,但如果OP的目标是“加密用户输入以生成密码字符串”,我不认为使用您自己的加密是一个好主意,因此,我不建议实际实现任何答案。很好地演示了为什么其他语言禁止将
%
与浮点数一起使用。虽然答案很好,但没有一个答案回答了在.NET 3.5和4之间发生了哪些变化导致不同行为的问题。+1指出了真正的问题,即问题中的代码是完全错误的
1230824813134842807283798520430636310264067713738977819859474030746648511411697029659004340261471771152928833391663821316264359104254030819694748088798262075483562075061997649