Estou criptografando a entrada do usuário para gerar uma string para senha. Mas uma linha de código fornece resultados diferentes em diferentes versões da estrutura. Código parcial com valor da tecla pressionada pelo usuário:
Tecla pressionada: 1. A variável ascii
é 49. Valor de 'e' e 'n' após alguns cálculos:
e = 103,
n = 143,
Math.Pow(ascii, e) % n
Resultado do código acima:
Em .NET 3.5 (C #)
Math.Pow(ascii, e) % n
dá
9.0
.Em .NET 4 (C #)
Math.Pow(ascii, e) % n
dá
77.0
.
Math.Pow()
fornece o (mesmo) resultado correto em ambas as versões.
Qual é a causa e existe uma solução?
%
com números de ponto flutuante.Respostas:
Math.Pow
funciona em números de ponto flutuante de precisão dupla; portanto, você não deve esperar que mais do que os primeiros 15 a 17 dígitos do resultado sejam precisos:No entanto, o módulo aritmético requer que todos os dígitos sejam precisos. No seu caso, você está computando 49 103 , cujo resultado consiste em 175 dígitos, tornando a operação do módulo sem sentido em ambas as suas respostas.
Para calcular o valor correto, você deve usar aritmética de precisão arbitrária, conforme fornecida pela
BigInteger
classe (introduzida no .NET 4.0).Edit : Conforme apontado por Mark Peters nos comentários abaixo, você deve usar o
BigInteger.ModPow
método, que se destina especificamente a este tipo de operação:fonte
1.0 - 0.9 - 0.1 == 0.0
avaliar parafalse
.Além do fato de que sua função de hashing não é muito boa * , o maior problema com seu código não é que ele retorna um número diferente dependendo da versão do .NET, mas que em ambos os casos ele retorna um número totalmente sem sentido: a resposta correta para o problema é
49 103 mod 143 = é 114. ( link para Wolfram Alpha )
Você pode usar este código para calcular esta resposta:
A razão pela qual seu cálculo produz um resultado diferente é que, para produzir uma resposta, você usa um valor intermediário que elimina a maioria dos dígitos significativos do número 49 103 : apenas os primeiros 16 de seus 175 dígitos estão corretos!
Os 159 dígitos restantes estão todos errados. A operação mod, no entanto, busca um resultado que exija que cada dígito esteja correto, incluindo os últimos. Portanto, mesmo a menor melhoria na precisão do
Math.Pow
que pode ter sido implementado no .NET 4, resultaria em uma diferença drástica do seu cálculo, o que essencialmente produz um resultado arbitrário.* Uma vez que esta questão fala sobre elevar inteiros a altas potências no contexto de hashing de senha, pode ser uma boa ideia ler este link de resposta antes de decidir se sua abordagem atual deve ser alterada por uma potencialmente melhor.
fonte
O que você vê é um erro de arredondamento em dobro.
Math.Pow
trabalha com double e a diferença é a seguinte:.NET 2.0 e 3.5 =>
var powerResult = Math.Pow(ascii, e);
retorna:.NET 4.0 e 4.5 =>
var powerResult = Math.Pow(ascii, e);
retorna:Observe o último dígito anterior
E
e isso está causando a diferença no resultado. Não é o operador de módulo(%)
.fonte
A precisão do ponto flutuante pode variar de máquina para máquina e até mesmo na mesma máquina .
Portanto, você não deve confiar nele para produzir resultados consistentes. Para criptografia, use as classes que o Framework fornece, em vez de criar suas próprias classes.
fonte
Há muitas respostas sobre como o código é ruim. No entanto, por que o resultado é diferente ...
As FPUs da Intel usam o formato de 80 bits internamente para obter mais precisão para resultados intermediários. Portanto, se um valor está no registro do processador, ele obtém 80 bits, mas quando é escrito na pilha, ele é armazenado em 64 bits .
Espero que a versão mais recente do .NET tenha um otimizador melhor em sua compilação Just in Time (JIT), portanto, ela mantém um valor em um registro em vez de gravá-lo na pilha e depois lê-lo de volta da pilha.
Pode ser que o JIT agora possa retornar um valor em um registro em vez de na pilha. Ou passe o valor para a função MOD em um registro.
Consulte também a pergunta sobre Stack Overflow Quais são os aplicativos / benefícios de um tipo de dados de precisão estendida de 80 bits?
Outros processadores, por exemplo, o ARM, darão resultados diferentes para este código.
fonte
Talvez seja melhor calculá-lo sozinho usando apenas aritmética de inteiros. Algo como:
Você pode comparar o desempenho com o desempenho da solução BigInteger postado nas outras respostas.
fonte