C#은 마우스 오버 및 중간 창의 부동 소수점과 컴파일된 부동 소수점을 어떻게 평가합니까?

StackOverflow https://stackoverflow.com/questions/1405486

  •  05-07-2019
  •  | 
  •  

문제

나는 사전에 double을 저장하는 데 이상한 점을 발견했으며 그 이유가 무엇인지 혼란 스럽습니다.

코드는 다음과 같습니다.

            Dictionary<string, double> a = new Dictionary<string, double>();
            a.Add("a", 1e-3);

            if (1.0 < a["a"] * 1e3)
                Console.WriteLine("Wrong");

            if (1.0 < 1e-3 * 1e3)
                Console.WriteLine("Wrong");

두 번째 if 문은 예상대로 작동합니다.1.0은 1.0보다 작지 않습니다.이제 첫 번째 if 문은 true로 평가됩니다.매우 이상한 점은 if 위로 마우스를 가져가면 Intellisense가 거짓이라고 알려주지만 코드는 행복하게 Console.WriteLine으로 이동한다는 것입니다.

이는 Visual Studio 2008의 C# 3.5용입니다.

부동 소수점 정확도 문제인가요?그렇다면 두 번째 if 문이 작동하는 이유는 무엇입니까?나는 여기서 매우 근본적인 것을 놓치고 있다고 생각합니다.

어떤 통찰력이라도 감사하겠습니다.

편집2 (질문을 조금 용도 변경):

나는 수학 정밀도 문제를 받아들일 수 있지만 지금 내 질문은 다음과 같습니다.마우스오버가 제대로 평가되는 이유는 무엇입니까?이는 중간 창에서도 마찬가지입니다.첫 번째 if 문의 코드를 중간 창에 붙여넣으면 false로 평가됩니다.

업데이트

우선, 모든 훌륭한 답변에 진심으로 감사드립니다.

또한 동일한 컴퓨터의 다른 프로젝트에서 이를 다시 만드는 데 문제가 있습니다.프로젝트 설정을 보면 차이가 없습니다.프로젝트 간의 IL을 보면 차이가 없습니다.분해를 보면 (메모리 주소 외에) 뚜렷한 차이점이 없습니다.그러나 원래 프로젝트를 디버깅하면 다음과 같은 결과가 나타납니다.screenshot of problem

직접 실행 창에서는 if가 false라고 알려주지만 코드는 조건부에 속합니다.

어쨌든, 내 생각에 가장 좋은 대답은 이러한 상황에서 부동 소수점 연산을 준비하는 것입니다.내가 이것을 놓을 수 없었던 이유는 런타임과 다른 디버거의 계산과 더 관련이 있습니다.매우 통찰력 있는 의견을 주신 Brian Gideon과 stephentyrone에게 진심으로 감사드립니다.

도움이 되었습니까?

해결책

떠 다니는 정밀 문제입니다.

두 번째 문은 컴파일러가 .exe를 방출하기 전에 표현식 1e-3 * 1e3을 계산하기 때문에 작동합니다.

Ildasm/Reflector에서 찾아 보면 다음과 같은 것을 방출합니다.

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");

다른 팁

여기서 문제는 매우 미묘합니다.C# 컴파일러는 사용자가 지정한 유형인 경우에도 계산을 이중으로 수행하는 코드를 (항상) 내보내지 않습니다.특히 중간 결과를 두 배로 반올림하지 않고 x87 명령어를 사용하여 "확장된" 정밀도로 계산을 수행하는 코드를 내보냅니다.

1e-3이 double 또는 long double로 평가되는지 여부와 곱셈이 double 또는 long double로 계산되는지 여부에 따라 다음 세 가지 결과 중 하나를 얻을 수 있습니다.

  • (long double)1e-3 * 1e3은 long double로 계산되어 1.0 - 엡실론입니다.
  • (double)1e-3 * 1e3 double로 계산하면 정확히 1.0입니다.
  • (double)1e-3 * 1e3은 long double로 계산되어 1.0 + 엡실론입니다.

분명히 첫 번째 비교, 즉 기대에 미치지 못하는 비교는 제가 나열한 세 번째 시나리오에 설명된 방식으로 평가되고 있습니다.1e-3은 저장했다가 다시 로드하여 반올림을 강제로 수행하거나 C#에서 1e-3을 배정밀도 리터럴로 인식하여 그런 식으로 처리하기 때문에 두 배로 반올림됩니다.곱셈은 ​​long double로 평가됩니다. C#에는 뇌사 수치 모델이 있습니다 이것이 컴파일러가 코드를 생성하는 방식입니다.

두 번째 비교의 곱셈은 다른 두 가지 방법 중 하나를 사용하여 평가되거나("1 > 1e-3 * 1e3"을 시도해 보면 알 수 있음) 컴파일러가 비교하기 전에 곱셈 결과를 반올림합니다. 컴파일 타임에 표현식을 평가할 때 1.0을 사용합니다.

일부 빌드 설정을 통해 알려주지 않고 확장 정밀도를 사용하지 않도록 컴파일러에 지시할 수 있습니다.codegen을 SSE2로 활성화하는 것도 작동할 수 있습니다.

답을 참조하십시오 여기

음 ... 이상합니다. 나는 당신의 문제를 재현 할 수 없습니다. C# 3.5 및 Visual Studio 2008도 사용하고 있습니다. 나는 당신의 예를 입력했습니다 바로 그거죠 게시되었고 나도 보이지 않습니다. Console.WriteLine 진술 실행.

또한 두 번째 IF 문은 컴파일러에 의해 최적화됩니다. ildasm/Reflector에서 디버그 및 릴리스 빌드를 모두 검토 할 때 증거는 없습니다. 그 이후로 컴파일러 경고를 받기 때문에 도달 할 수없는 코드가 감지되었다고 말하기 때문입니다.

마지막으로, 나는 이것이 어쨌든 부동 소수점 정밀 문제가 될 수있는 방법을 알지 못합니다. C# 컴파일러가 런타임에서 CLR과 다르게 두 개의 복식을 정적으로 평가하는 이유는 무엇입니까? 그것이 실제로 그런 경우라면 C# 컴파일러에 버그가 있다는 주장을 할 수 있습니다.

편집하다: 이것을 조금 더 준 후에 나는 이것이 ~ 아니다 부동 소수점 정밀 문제. 컴파일러 또는 디버거의 버그를 우연히 발견했거나 게시 한 코드는 실행중인 실제 코드를 정확하게 대표하지 않아야합니다. 컴파일러의 버그에 대해 회의적이지만 디버거의 버그가 더 가능성이 높습니다. 프로젝트를 재건하고 다시 실행해보십시오. Exe와 함께 컴파일 된 디버깅 정보가 동기화되지 않은 것일 수도 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top