[c#] 모듈로 연산자 (%)는 C #의 다른 .NET 버전에 대해 다른 결과를 제공합니다.

비밀번호 문자열을 생성하기 위해 사용자 입력을 암호화하고 있습니다. 그러나 한 줄의 코드는 프레임 워크의 다른 버전에서 다른 결과를 제공합니다. 사용자가 누른 키 값이있는 부분 코드 :

키 누름 : 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 자리 이상 이 정확할 것으로 기 대해서는 안됩니다.

또한 모든 부동 소수점 숫자에는 제한된 수의 유효 자릿수가 있으며, 이는 부동 소수점 값이 실수에 근접하는 정도를 결정합니다. Double17 자릿수의 최대 내부적으로 유지되지만 값은, 정밀도의 15 진수까지 갖는다.

그러나 모듈로 산술에서는 모든 숫자가 정확해야합니다. 귀하의 경우에는, 당신은 49 계산된다 (103) , 그 결과 175 자리 숫자로 구성되어, 모두 답의 모듈로 연산의 의미를 제작.

올바른 값을 계산하려면 BigInteger클래스 (.NET 4.0에서 도입 됨)에서 제공하는 임의 정밀도 산술을 사용해야합니다 .

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

편집 : 아래 주석에서 Mark Peters가 지적했듯이 BigInteger.ModPow이러한 종류의 작업을 위해 특별히 고안된 방법을 사용해야합니다 .

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


답변

해싱 함수가별로 좋지 않다는 사실을 제외하고 * 코드의 가장 큰 문제는 .NET 버전에 따라 다른 숫자를 반환하는 것이 아니라 두 경우 모두 완전히 의미없는 숫자를 반환한다는 것입니다. 문제에 대한 정답은

49 103 = 114이 (143을 개조 볼프람 알파 링크 )

이 코드를 사용하여이 답변을 계산할 수 있습니다.

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;
}

귀하의 계산이 다른 결과를 생성하는 이유는 대답을 생산하기 위해, 당신은 (49)의 유효 숫자의 대부분 떨어 중간 값 사용한다는 것입니다 (103) 에만 175 자리의 첫 번째 16이 올바른 : 수를!

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가 결과의 차이를 유발합니다. 모듈러스 연산자가 아닙니다 (%) .


답변

부동 소수점 정밀도는 시스템마다 다를 수 있으며 동일한 시스템에서도 다를 수 있습니다 .

그러나 .NET은 앱을위한 가상 머신을 생성하지만 버전마다 변경 사항이 있습니다.

따라서 일관된 결과를 생성하기 위해 의존해서는 안됩니다. 암호화의 경우 자체적으로 롤링하는 대신 프레임 워크에서 제공하는 클래스를 사용하십시오.


답변

코드가 나쁜 방식에 대한 많은 답변이 있습니다. 하지만 결과가 다른 이유는…

Intel의 FPU 는 내부적으로 80 비트 형식을 사용하여 중간 결과에 대해 더 많은 정밀도를 얻습니다. 따라서 값이 프로세서 레지스터에 있으면 80 비트를 얻지 만 스택에 기록되면 64 비트에 저장됩니다 .

최신 버전의 .NET은 JIT (Just in Time) 컴파일에서 더 나은 최적화 프로그램을 가지고 있으므로 스택에 기록한 다음 스택에서 다시 읽는 대신 레지스터에 값을 유지합니다.

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 솔루션의 성능과 성능을 비교할 수 있습니다.


답변