문제

C# (.NET 3.5 SP1)의 다음 코드는 내 컴퓨터의 무한 루프입니다.

for (float i = 0; i < float.MaxValue; i++) ;

16777216.0 + 1은 16777216.0으로 평가되었습니다. 그러나이 시점에서 : i + 1! = i.

이것은 약간의 미친 짓입니다.

부동 소수점 수가 저장되는 방식에 부정확성이 있다는 것을 알고 있습니다. 그리고 나는 전체 숫자가 플로트로 제대로 저장 될 수없는 것보다 2^24 더 큰 것을 읽었습니다.

위의 코드는 여전히 숫자를 제대로 표현할 수 없더라도 C#에서 유효해야합니다.

왜 작동하지 않습니까?

당신은 두 배로 똑같이 일어날 수 있지만 아주 오랜 시간이 걸립니다. 9007199254740992.0은 더블에 대한 한계입니다.

도움이 되었습니까?

해결책

그렇습니다. 문제는 플로트에 하나를 추가하려면

16777217.0

이것은 이것이 무선의 경계에 있으며 플로트로 정확하게 표현 될 수 없습니다. (이용 가능한 다음으로 가장 높은 가치는입니다 16777218.0)

그래서 그것은 가장 가까운 대표 플로트로 반올림합니다

16777216.0

이렇게 말해 드리겠습니다.

당신은 가지고 있기 때문에 떠 있는 정밀도의 양이 높고 높은 수를 증가시켜야합니다.

편집하다:

좋아, 이것은 설명하기가 조금 어렵지만 이것을 시도하십시오.

float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);

1.0F의 차이를 나타내려면 128 비트 이상의 정밀도가 필요하기 때문에 그 값에서는 괜찮습니다. 플로트에는 비트가 32 개 밖에되지 않습니다.

edit2

내 계산에 따르면 최소 128 자리 숫자 서명되지 않았습니다 필요합니다.

log(3.40282347E+38) * log(10) / log(2) = 128

문제에 대한 해결책으로 두 개의 128 비트 숫자를 살펴볼 수 있습니다. 그러나 이것은 완료하는 데 적어도 10 년이 걸릴 것입니다.

다른 팁

예를 들어 플로팅 포인트 번호는 최대 2 개의 중요한 소수점 자리와 지수로 표시된다고 상상해보십시오.이 경우 정확히 0에서 99까지 계산할 수 있습니다. 다음은 100이지만 "2의 전력에 1.0 배 10"으로 저장되는 2 개의 중요한 숫자 만 가질 수 있기 때문입니다. 하나를 추가하면 ... 뭐야?

기껏해야 중간 결과로서 101이 될 것이며, 실제로는 (무의미한 3 자리를 폐기하는 반올림 오류를 통해) 다시 "1.0 배의 전력으로 2"로 저장됩니다.

무엇이 잘못되고 있는지 이해하려면 IEEE 표준을 읽어야합니다. 부동 소수점

a의 구조를 살펴 보겠습니다 부동 소수점 잠시 숫자 :

부동 소수점 번호는 두 부분으로 나뉩니다 (OK 3이지만 부호 비트는 1 초 동안 무시합니다).

당신은 지수와 Mantissa가 있습니다. 그렇게 :

smmmmmmmmeeeeeee

참고 : 비트 수에 비해 분명하지는 않지만 무슨 일이 일어나고 있는지에 대한 일반적인 아이디어를 제공합니다.

당신이 가진 숫자를 알아 내기 위해 다음 계산을 수행합니다.

mmmmmm * 2^(eeeeee) * (-1)^s

그렇다면 float.maxvalue는 무엇입니까? 글쎄, 당신은 가능한 최대 만티 사와 가장 큰 지수를 가질 것입니다. 이렇게 보인다.

01111111111111111

실제로 우리는 NAN과 +-INF와 몇 가지 다른 규칙을 정의하지만 질문과 관련이 없기 때문에 1 초 동안 무시합니다.

그래서, 당신이 가면 어떻게되는지 9.9999*2^99 + 1? 글쎄, 당신은 1을 추가 할 유의미한 수치가 충분하지 않습니다. 결과적으로 그것은 같은 숫자로 반올림됩니다. 단일 부동 소수점의 경우 정밀 +1 둥글게 시작됩니다 16777216.0

오버플로와는 아무런 관련이 없거나 최대 값 근처에 있습니다. 16777216.0의 플로트 값은 16777216의 이진 표현을 가지고 있습니다. 그런 다음 1 씩 증가하므로 1677217.0의 이진 표현이 16777216이라는 점을 제외하고는 1677217.0이어야합니다! 따라서 실제로는 증가하지 않거나 최소한 증분이 기대하는 것을 수행하지 않습니다.

다음은 Jon Skeet이 작성한 수업입니다.

DoubleConverter.cs

이 코드를 사용해보십시오.

double d1 = 16777217.0;
Console.WriteLine(DoubleConverter.ToExactString(d1));

float f1 = 16777216.0f;
Console.WriteLine(DoubleConverter.ToExactString(f1));

float f2 = 16777217.0f;
Console.WriteLine(DoubleConverter.ToExactString(f2));

16777216.0의 내부 표현이 어떻게 같은 16777217.0인지 주목하십시오 !!

내가 float.maxvalue에 접근 할 때의 반복은이 값 바로 아래에 있습니다. 다음 반복은 i에 추가되지만 float.maxvalue보다 더 큰 숫자를 유지할 수는 없습니다. 따라서 값이 훨씬 작고 루프를 다시 시작합니다.

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