부동 소수점 변수의 범위가 값에 영향을 줍니까?
콘솔 애플리케이션에서 다음 C # 코드를 실행하면 The sums are Not equal.
줄의 주석을 해제 한 후 실행하면 다음 System.Console.WriteLine()과 같은 메시지가 표시됩니다 The sums are equal.
static void Main(string[] args)
{
float f = Sum(0.1f, 0.2f);
float g = Sum(0.1f, 0.2f);
//System.Console.WriteLine("f = " + f + " and g = " + g);
if (f == g)
{
System.Console.WriteLine("The sums are equal");
}
else
{
System.Console.WriteLine("The sums are Not equal");
}
}
static float Sum(float a, float b)
{
System.Console.WriteLine(a + b);
return a + b;
}
이 동작의 실제 이유는 무엇입니까?
범위와 관련이 없습니다. 스택 역학과 부동 소수점 처리의 조합입니다. 컴파일러에 대한 일부 지식은 이러한 반 직관적 인 동작을 명확하게하는 데 도움이됩니다.
이 때 Console.WriteLine, 값을 주석 f및 g평가 스택과 숙박에 거기 당신이 당신의 Main 메서드의 평등 테스트를 통과 한 후에 때까지.
하면 Console.Writeline, 값 댓글되지 f및 g호출의 순간 호출 스택을 계산 스택에서 이동하는 때 계산 스택을 복원 할 Console.WriteLine복귀한다. 그리고 당신의 비교 if (f == g)는 나중에 이루어집니다. 이 값을 호출 스택에 저장하는 동안 일부 반올림이 발생할 수 있으며 일부 정보가 손실 될 수 있습니다.
당신이 호출 할 시나리오에서 Console.WriteLine의 f과 g비교 테스트에서 같은 값은 없습니다. 가상 머신에 의해 정밀도 및 반올림 규칙이 다른 형식으로 복사 및 복원되었습니다.
특정 코드에서의 호출 Console.WriteLine이 주석 처리되면 평가 스택이 호출 스택에 저장되지 않으며 반올림이 발생하지 않습니다. 플랫폼의 구현이 평가 스택에서 향상된 정밀도를 제공하도록 허용되기 때문에 이러한 불일치가 발생할 수 있습니다.
편집 이 경우 우리가 치는 것은 CLI 사양에 의해 허용됩니다. 섹션 I.12.1.3에서 다음과 같이 읽습니다.
부동 소수점 숫자 (정적, 배열 요소 및 클래스 필드)의 저장 위치는 고정 된 크기입니다. 지원되는 저장소 크기는 float32 및 float64입니다. 다른 모든 곳 (평가 스택에서 인수, 반환 유형 및 지역 변수로) 부동 소수점 숫자는 내부 부동 소수점 유형을 사용하여 표현됩니다. 각 인스턴스에서 변수 또는 표현식의 명목 유형은 float32 또는 float64이지만 그 값은 추가 범위 및 / 또는 정밀도로 내부적으로 나타낼 수 있습니다. 내부 부동 소수점 표현의 크기는 구현에 따라 다르며 다양 할 수 있으며 적어도 표현되는 변수 또는 표현의 정밀도만큼 큰 정밀도를 가져야합니다.
이 인용구의 키워드는 "구현에 따라 다름"이며 "다를 수 있습니다". OP의 경우 구현이 실제로 다양하다는 것을 알 수 있습니다.
Java 플랫폼의 비 strictfp 부동 소수점 산술에도 관련 문제가 있습니다. 자세한 내용 은 JVM의 부동 소수점 연산이 모든 플랫폼에서 동일한 결과를 제공합니까?
이 행동의 실제 이유는 무엇입니까?
이 특정 경우에 무슨 일이 일어나고 있는지에 대한 세부 정보를 제공 할 수는 없지만 일반적인 문제와 사용으로 Console.WriteLine인해 상황이 변경되는 이유 는 이해합니다 .
이전 게시물 에서 보았 듯이 때때로 변수 유형에 지정된 것보다 더 높은 정밀도로 부동 소수점 유형에 대해 연산이 수행됩니다. 지역 변수의 경우 메서드 실행 중에 값이 메모리에 저장되는 방식을 포함 할 수 있습니다.
귀하의 경우에는 다음과 같이 의심됩니다.
Sum방법은 인라인 (나중에 참조)되고- 합계 자체는 예상했던 32 비트 부동 소수점보다 더 높은 정밀도로 수행됩니다.
- 변수 중 하나 의 값 (
f예 : 고정밀 레지스터에 저장 됨)- 이 변수의 경우 "더 정확한"결과가 직접 저장됩니다.
- 다른 변수 (
g) 의 값은 스택에 32 비트 값으로 저장됩니다.- for this variable, the "more precise" result is being reduced to 32 bits
- when the comparison is performed, the variable on the stack is being promoted to a higher-precision value and compared with the other higher-precision value, and the difference is due to one of them having previously lost information and the other not
When you uncomment the Console.WriteLine statement, I'm guessing that (for whatever reason) forces both variables to be stored in their "proper" 32-bit precision, so they're both being treated the same way.
This hypothesis is all somewhat messed up by the fact that adding
[MethodImpl(MethodImplOptions.NoInlining)]
... does not change the result as far as I can see. I may be doing something else wrong along those lines though.
Really, we should look at the assembly code which is executing - I don't have the time to do that now, unfortunately.
(Not a real answer but hopefully some supporting documentation)
Configuration: Core i7, Windows 8.1, Visual Studio 2013
Platform x86:
Version Optimized Code? Debugger Enabled? Outcome
4.5.1 Yes No Not equal
4.5.1 Yes Yes Equal
4.5.1 No No Equal
4.5.1 No Yes Equal
2.0 Yes No Not Equal
2.0 Yes Yes Equal
2.0 No No Equal
2.0 No Yes Equal
Platform x64:
Version Optimized Code? Debugger Enabled? Outcome
4.5.1 Yes No Equal
4.5.1 Yes Yes Equal
4.5.1 No No Equal
4.5.1 No Yes Equal
2.0 Yes No Equal
2.0 Yes Yes Equal
2.0 No No Equal
2.0 No Yes Equal
The situation only seems to occur with optimized code on x86 configurations.
'IT Share you' 카테고리의 다른 글
| NSDateFormatter에서 'YYYY'와 'yyyy'의 차이점 (0) | 2020.12.01 |
|---|---|
| 부모를 채우기 위해 인라인 블록 요소의 높이 가져 오기 (0) | 2020.12.01 |
| AssertionError 란 무엇입니까? (0) | 2020.12.01 |
| Ruby : 공개 정적 메서드를 만드는 방법은 무엇입니까? (0) | 2020.11.30 |
| JavaFX-실제로 브라우저에 배포 할 수 있습니까? (0) | 2020.11.30 |