Я шифрую вводимые пользователем данные для создания строки пароля. Но строка кода дает разные результаты в разных версиях фреймворка. Частичный код со значением нажатой пользователем клавиши:
Нажата клавиша: 1. Переменная ascii
- 49. Значения "e" и "n" после некоторых вычислений:
e = 103,
n = 143,
Math.Pow(ascii, e) % n
Результат приведенного выше кода:
В .NET 3.5 (C #)
Math.Pow(ascii, e) % n
дает
9.0
.В .NET 4 (C #)
Math.Pow(ascii, e) % n
дает
77.0
.
Math.Pow()
дает правильный (одинаковый) результат в обеих версиях.
В чем причина и есть ли решение?
%
чисел с плавающей запятой.Ответы:
Math.Pow
работает с числами с плавающей запятой двойной точности; таким образом, вы не должны ожидать, что более первых 15–17 цифр результата будут точными:Однако арифметика по модулю требует, чтобы все цифры были точными. В вашем случае вы вычисляете 49103 , результат которого состоит из 175 цифр, что делает операцию по модулю бессмысленной в обоих ваших ответах.
Чтобы вычислить правильное значение, вы должны использовать арифметику произвольной точности, как это предусмотрено
BigInteger
классом (введенным в .NET 4.0).int val = (int)(BigInteger.Pow(49, 103) % 143); // gives 114
Изменить : как указал Марк Петерс в комментариях ниже, вы должны использовать
BigInteger.ModPow
метод, который предназначен специально для такого рода операций:int val = (int)BigInteger.ModPow(49, 103, 143); // gives 114
источник
1.0 - 0.9 - 0.1 == 0.0
вычисление tofalse
.Помимо того, что ваша функция хеширования не очень хороша * , самая большая проблема с вашим кодом заключается не в том, что он возвращает другое число в зависимости от версии .NET, а в том, что в обоих случаях он возвращает совершенно бессмысленное число: правильный ответ на проблему
49 103 мод 143 = является 114. ( ссылка Wolfram Alpha )
Вы можете использовать этот код для вычисления этого ответа:
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 : только первые 16 из его 175 цифр верны!
1230824813134842807283798520430636310264067713738977819859474030746648511411697029659004340261471771152928833391663821316264359104254030819694748088798262075483562075061997649
Остальные 159 цифр неверны. Однако операция mod ищет результат, требующий, чтобы каждая цифра была правильной, включая самые последние. Следовательно, даже малейшее улучшение точности,
Math.Pow
которое могло быть реализовано в .NET 4, привело бы к резкому изменению ваших вычислений, что по существу дает произвольный результат.* Поскольку в этом вопросе говорится о повышении целых чисел до высоких степеней в контексте хеширования паролей, может быть очень хорошей идеей прочитать эту ссылку для ответа, прежде чем решить, следует ли изменить ваш текущий подход на потенциально лучший.
источник
Вы видите ошибку округления в два раза.
Math.Pow
работает с двойным, и разница следующая:.NET 2.0 и 3.5 =>
var powerResult = Math.Pow(ascii, e);
возвращает:1.2308248131348429E+174
.NET 4.0 и 4.5 =>
var powerResult = Math.Pow(ascii, e);
возвращает:1.2308248131348427E+174
Обратите внимание на последнюю цифру перед цифрой,
E
которая вызывает разницу в результате. Это не оператор модуля(%)
.источник
Точность с плавающей запятой может варьироваться от машины к машине и даже на одной машине .
Поэтому не следует полагаться на него для получения стабильных результатов. Для шифрования используйте классы, предоставляемые Framework, вместо того, чтобы создавать собственные.
источник
Есть много ответов о том, что код плох. Однако, почему результат другой…
FPU Intel внутренне используют 80-битный формат для получения большей точности промежуточных результатов. Таким образом, если значение находится в регистре процессора, оно получает 80 бит, но когда оно записывается в стек, оно сохраняется в 64 битах .
Я ожидаю, что новая версия .NET имеет лучший оптимизатор при компиляции Just in Time (JIT), поэтому он сохраняет значение в регистре, а не записывает его в стек, а затем считывает его обратно из стека.
Возможно, JIT теперь может возвращать значение в регистре, а не в стеке. Или передайте значение функции MOD в регистре.
См. Также вопрос о переполнении стека. Каковы приложения / преимущества 80-битного типа данных расширенной точности?
Другие процессоры, например ARM, для этого кода дадут другие результаты.
источник
Может быть, лучше рассчитать его самостоятельно, используя только целочисленную арифметику. Что-то типа:
int n = 143; int e = 103; int result = 1; int ascii = (int) 'a'; for (i = 0; i < e; ++i) result = result * ascii % n;
Вы можете сравнить производительность с производительностью решения BigInteger, опубликованного в других ответах.
источник