Estoy encriptando la entrada del usuario para generar una cadena de contraseña. Pero una línea de código da diferentes resultados en diferentes versiones del marco. Código parcial con valor de tecla presionada por usuario:
Tecla presionada: 1. La variable ascii
es 49. Valor de 'e' y 'n' después de algún cálculo:
e = 103,
n = 143,
Math.Pow(ascii, e) % n
Resultado del código anterior:
En .NET 3.5 (C #)
Math.Pow(ascii, e) % n
da
9.0
.En .NET 4 (C #)
Math.Pow(ascii, e) % n
da
77.0
.
Math.Pow()
da el resultado correcto (el mismo) en ambas versiones.
¿Cuál es la causa? ¿Existe una solución?
%
números de punto flotante.Respuestas:
Math.Pow
trabaja con números de coma flotante de doble precisión; por lo tanto, no debe esperar que más de los primeros 15 a 17 dígitos del resultado sean precisos:Sin embargo, la aritmética de módulo requiere que todos los dígitos sean precisos. En su caso, está calculando 49103 , cuyo resultado consta de 175 dígitos, lo que hace que la operación de módulo no tenga sentido en ambas respuestas.
Para calcular el valor correcto, debe usar aritmética de precisión arbitraria, como lo proporciona la
BigInteger
clase (introducida en .NET 4.0).Editar : Como señaló Mark Peters en los comentarios a continuación, debe usar el
BigInteger.ModPow
método, que está diseñado específicamente para este tipo de operación:fuente
1.0 - 0.9 - 0.1 == 0.0
evaluar afalse
.Aparte del hecho de que su función hash no es muy buena * , el mayor problema con su código no es que devuelve un número diferente dependiendo de la versión de .NET, sino que en ambos casos devuelve un número completamente sin sentido: la respuesta correcta al problema es
49 103 mod 143 = es 114. ( enlace a Wolfram Alpha )
Puede usar este código para calcular esta respuesta:
La razón por la que su cálculo produce un resultado diferente es que para producir una respuesta, usa un valor intermedio que elimina la mayoría de los dígitos significativos del número 49103 : ¡solo los primeros 16 de sus 175 dígitos son correctos!
Los 159 dígitos restantes están mal. Sin embargo, la operación de modificación busca un resultado que requiera que todos los dígitos sean correctos, incluidos los últimos. Por lo tanto, incluso la más mínima mejora en la precisión de
Math.Pow
eso puede haberse implementado en .NET 4, daría como resultado una diferencia drástica de su cálculo, que esencialmente produce un resultado arbitrario.* Dado que esta pregunta habla de aumentar los números enteros a poderes altos en el contexto del hash de contraseñas, puede ser una muy buena idea leer este enlace de respuesta antes de decidir si su enfoque actual debe cambiarse por uno potencialmente mejor.
fuente
Lo que ves es un error de redondeo al doble.
Math.Pow
funciona con doble y la diferencia es la siguiente:.NET 2.0 y 3.5 =>
var powerResult = Math.Pow(ascii, e);
devuelve:.NET 4.0 y 4.5 =>
var powerResult = Math.Pow(ascii, e);
devuelve:Observe el último dígito antes
E
y eso está causando la diferencia en el resultado. No es el operador de módulo(%)
.fuente
La precisión del punto flotante puede variar de una máquina a otra, e incluso en la misma máquina .
Por lo tanto, no debe confiar en él para producir resultados consistentes. Para el cifrado, utilice las clases que proporciona Framework en lugar de utilizar las suyas propias.
fuente
Hay muchas respuestas sobre la forma en que el código es malo. Sin embargo, en cuanto a por qué el resultado es diferente ...
Las FPU de Intel utilizan el formato de 80 bits internamente para obtener más precisión en los resultados intermedios. Entonces, si un valor está en el registro del procesador, obtiene 80 bits, pero cuando se escribe en la pila, se almacena en 64 bits. .
Espero que la versión más nueva de .NET tenga un mejor optimizador en su compilación Just in Time (JIT), por lo que mantiene un valor en un registro en lugar de escribirlo en la pila y luego leerlo de la pila.
Puede ser que el JIT ahora pueda devolver un valor en un registro en lugar de en la pila. O pase el valor a la función MOD en un registro.
Consulte también la pregunta de desbordamiento de pila ¿ Cuáles son las aplicaciones / beneficios de un tipo de datos de precisión extendida de 80 bits?
Otros procesadores, por ejemplo, el ARM, darán resultados diferentes para este código.
fuente
Tal vez sea mejor calcularlo usted mismo usando solo aritmética de números enteros. Algo como:
Puede comparar el rendimiento con el rendimiento de la solución BigInteger publicada en las otras respuestas.
fuente