부동점수로 인한 사고

다음 코드의 출력 값은 얼마입니까?
void main()
{
    float data = 266270.83;
    printf("data=%0.2f
",data); }

내 32비트 가상 기기에서 출력 값은 데이터=266270.84이다
왜 이런 상황이 생겼을까, 컴퓨터에 문제가 생겼을까.
물론 컴퓨터에 문제가 생긴 것은 아니다. 우리는 아래의 몇 가지 점에서 분석하고 토론한다.
우선, 우리는 부동점수가 메모리에 저장된 형식에서 분석한다.
모두가 알다시피 데이터는 메모리에서 010101이라는 2진법의 형식으로 저장된다. 그 부동점수가 정수에 비해 저장되는 데는 또 어떤 특수한 점이 있는가.
과학 계수법에 따르면 부동점수는 메모리에서 세 부분으로 나뉘어 저장되는데 각각 기호위, 지수위, 꼬리수위이다.
float: 단정밀 부동점수, 1위 기호위, 8위 지수위, 23위 꼬리수위, 총 32위
더블: 더블 정밀도 부동점수, 1위 기호 비트, 11위 지수 비트, 52위 꼬리 비트, 총 64위.
그러므로 우리는 부동점수를 먼저 2진법으로 바꾸어 다음과 같은 규칙을 따라야 한다
1. 정수 부분과 소수 부분을 각각 이진 형식으로 변환한다.
2. 정수 부분을 2진법으로 변환하여'2를 제외하고 나머지를 추출하고 역순으로 배열한다'는 법을 사용한다. 즉, 정수 부분을 2로 나누고 나머지를 2로 나누어 0을 얻을 때까지 나눈 다음에 나머지를 역순으로 배열한다.
3. 소수 부분을 2진법으로 변환하면'곱하기 2는 정돈, 순서 배열'법, 즉 소수 부분에 2를 곱한 다음에 정수 부분을 꺼내고 나머지 소수 부분은 계속 2를 곱하여 소수 부분이 0이 될 때까지 한다.영원히 0이 되지 않으면 충분한 자릿수의 소수를 보류하고 나머지 자릿수는 반올림(0반올림)하고 꺼낸 정수 부분의 순서대로 배열해야 한다.
4. 변환된 이진 부동점수는 소수점 이동 작업을 하고 소수점을 정수 자리로 한 자리만 옮긴다.
5. 지수 위치 데이터 계산: 단정밀 부동점수 지수 계산은 편이 위치 계산(2^(e-1)-1, e는 지수 위치), 편이 위치 127, 4단계의 소수점 이동 위치 + 편이 위치 = 실제 지수 위치 데이터
 
예:266270.83
1. 정수 부분: 266270은 바이너리 1000001 0000 0001 1110을 나타냅니다.
2. 소수 부분: 0.83은 2진법 1101 0100 0111을 나타낸다.
3. 이진 부동점수: 1000001 0000 0001 1110.1101  0100  0111…………
4. 2진법 부동점수 소수점 이동 작업: 총 이동(18자리) 2진법은 ---10010
5. 위치 이동 후 소수 표시: 1.00 0001 0000 0001 1110 1101 0100 0111......
6. 지수 부분 계산: 011111+10010 = 1001 0001
7. 부동점수가 메모리에 표시되는 형식:
기호 비트
지수 비트
끝자리

100 1000 1
000  0010  0000  0011  1101  1010
버리는 자리가 1000111이니까...
0반올림법에 따르면 반올림의 가장 높은 위치는 1이기 때문에 가장 낮은 것은 1을 더해야 하기 때문에 최종적으로 메모리에 다음과 같은 형식을 나타낸다.
기호 비트
지수 비트
끝자리

100 1000 1
000  0010  0000  0011  1101  1011
8. 부동점수 메모리에 표시된 형식에 따라 반추한다
0 기호 비트
10001 지수(18)
.000 0010 0000 0011 1101 1011 끝자리
끝 소수점 오른쪽으로 18자리 이동
000  0010  0000  0011  110.1  1011
 
0.   1       1          0       1               1
    0.5    0.25      0    0.0625    0.03125
계산 결과 최종: 266270.84375
두 자리의 소수를 인쇄하기 때문에 마지막에 인쇄하는 것은 문장이 인쇄하기 시작한 266270.84와 같다
 
해결 방법:
이런 상황을 만나면 단정밀도 부동점수는 이미 정밀도 문제가 발생했음을 나타낸다. 그러면 우리는 이중정밀도 부동점수로 표시하면 이 문제를 해결할 수 있다.
 
간단한 단정밀도 부동점수 정밀도 문제 판단
우리는 단정밀도 부동점수 소수 자릿수가 6자리이고 대응 2진법은 24자리라는 것을 알고 있다. 즉, 한 개의 유효 소수를 나타내려면 적어도 4자리의 2진법을 차지한다.그러면 우리는 정수 자리의 2진법 길이와 소수 정밀도 위치에 따라 단정밀도 부동점수의 정밀도에 문제가 있는지 간단하게 확정할 수 있다.
예: 266270.83,
정수 부분: 1000001 00000 0001 1110 19-1 = 18비트 차지
소수 부분: 대응하는 두 자리 정밀도 소수, 2*4=8자리 필요
합계: 정수+소수 18+8=26>24, 그래서 두 소수의 정밀도에 문제가 있을 수 있습니다
예: 16384.001
정수 부분: 100 0000 0000 0000 15-1 = 14비트 차지
소수 부분: 대응하는 세 자리 정밀도 소수, 3*4=12자리 필요
합계: 정수+소수 14+12=26>24, 그래서 세 자리 소수의 정밀도에 문제가 있을 수 있습니다
void main()
{
float x= 166384.001;
float y = 0.001;
float z;
z = x+y;
printf("data=%0.3f,%0.3f,%0.3f
",x,y,z); }

여러분은 이 코드를 시험해 보셔도 됩니다. 결과는 매우 놀랍습니다.

좋은 웹페이지 즐겨찾기