C++에서 double 부동 소수점 정밀도 손실 에 대한 심도 있 는 분석

C/C++부동 소수점 에 관 한 블 로 그 를 보 았 습 니 다.Win 32 에서 int,포인터 주소,long 등 4 바이트 정 수 를 double 에 부여 한 다음 에 이 double 수로 원시 유형의 수 를 부 여 했 습 니 다.결 과 는 최초의 수치 와 일치 합 니 다.즉,정밀도 손실 이 존재 하지 않 습 니 다.예 를 들 어 다음 결 과 는 항상 true:

 long a=123456; //assign any long number here
 double db=a;
 long b=db;
 printf("%s
",a==b?"true":"false");
그러나 롱 롱 이나 win 64 의 포인터 주소 등 8 바이트 의 정 수 는 정밀도 가 없어 지기 때문에 이 방면 에 대해 간단 한 테스트 를 실시 했다.

#include<iostream>
#include<stdlib.h>

void showEncodeOfDouble(unsigned char* db){

 const int ByteLength=8; 
 for(int i=ByteLength-1;i>=0;i--) 
  printf(" %.2x",db[i]);

 printf("
"); } int main(){ unsigned long long maxULL=0xffffffffffffffff; //2^64-1=18446744073709551615, //max unsigned long long printf("%llu
",maxULL); double d1=maxULL; //20bit Significant,Precision Loss printf("%f
",d1); maxULL=d1; printf("%llu
",maxULL); showEncodeOfDouble((unsigned char*)&d1); system("pause"); return 0; }
출력 결 과 는 다음 과 같 습 니 다(visual studio,win 32).
18446744073709551615
18446744073709552000.000000
9223372036854775808
 43 f0 00 00 00 00 00 00
이로써 두 가지 의문 이 생 겼 다.
  1)왜 정밀 도 를 잃 어 버 린 후 얻 은 double 수 는 18446744073709552000.00000 입 니까?
  2)왜 double 수 를 unsigned long 으로 다시 바 꾼 후에 얻 은 수 는 double 과 일치 하지 않 습 니까?
이 두 가지 문제 에 대해 서 는 C+부동 소수점 의 규격 에 대해 어느 정도 알 아야 한다.
1  IEEE 부동 소수점 표준
C/C++는 IEEE 부동 소수점 기준 을 사용 하 는데'이 진 과학 표현 법'으로 작은 수 를 표시 합 니 다.

그 중에서 M 은 정수 부분 에 한 자리 만 있 는 이 진 소수 이다.예 를 들 어 1.011 은 10 진법 의 1.375 를 나타 낸다.E 는 이 소수 가 2 를 밑 으로 할 때의 단 계 를 나타 낸다.이상 의 표현 방식 을 바탕 으로 소 수 는 세 부분 을 인 코딩 해 야 한다.기 호 를 나타 내 는 s 와 단계 코드 E,꼬리 코드 M 이다.C++의 double 형식 세 가지 인 코딩 이 차지 하 는 자릿수 는 그림 과 같다.

53 비트 꼬리 디지털 이 도달 할 수 있 는 정밀 도 는 53 2 진법 으로 약 16 개의 10 진법(53 log 10(2)≈15.955)이다.[1]꼬리 디지털 의 인 코딩 에는 숨겨 진 시작 정수 1(또는 0,11 개의 단계 코드 가 0 일 때)이 있 기 때문에 실제 적 으로 15-17 비트 10 진법 의 정밀 도 를 얻 을 수 있다.유효 자릿수 최대 15 비트 의 10 진수 가 double 로 바 뀌 었 다가 원래 의 10 진수 형식 으로 다시 바 뀌 었 을 때 수치 가 일치 합 니 다.다른 한편,하나의 double 수 를 17 자리 이상 의 유효 숫자 를 수용 할 수 있 는 10 진수 로 바 꾸 고 다시 double 로 바 꾸 면 결과 수치 도 일치 합 니 다.
이 는 4 바이트 의 정수 가 double 로 바 뀌 면 일치(2^32=4294967296 은 10 개의 유효 비트)를 유지 할 수 있 고 8 바이트 의 정 수 는 정밀도(2^64-1=18446744073709551615 총 20 개의 유효 비트)를 잃 을 수 있 는 이 유 를 설명 한다.그러나 첫 번 째 문제 에서 정수 가 정 도 를 잃 어 버 린 후에 더 블 수치 로 바 뀌 는 것 은 어떻게 된 것 일 까?이것 은 C++단계 코드 와 꼬리 수가 더 블 수치 에 대한 의 미 를 알 아야 한다.
2 단계 코드 인 코딩 과 끝자리 인 코딩
단계 코드 인 코딩 에서 상수 편향 량 Bias=1023 이 있 습 니 다.11 비트 단계 코드 가 대표 하 는 부호 없 는 정수 치 를 e 로 가정 합 니 다.
1)e 가 0(11 자리 가 모두 1 일 때 특수 숫자 를 표시 하 는 데 사용 되 지 않 으 면 double 수 치 는?

2)e=0 이면 작은 수 치 는?
 
그러면 함수 showEncodeOfDouble 을 볼 수 있 습 니 다.그 역할 은 double 수의 인 코딩 을 바이트 에 따라 인쇄 하 는 것 입 니 다(왼쪽 은 높 은 바이트).인쇄 결 과 를 위 에서 계산 하면 double 인 코딩 값 이 2^64 를 나타 내 는 것 을 알 수 있 습 니 다.이것 은 합 리 적 입 니 다.정밀도 가 높 은 정 수 를 double 로 바 꿀 때 C+는 짝수 에 반올림 하 는 방식 으로 가장 가 까 운 값 을 얻 을 수 있 습 니 다[2].인쇄 된 결 과 는 C++부동 소수점 인쇄 에 있 는 자세 한 문제 입 니 다.
3 C++부동 소수점 인쇄
많은 C/C++라 이브 러 리 에서 double 을 출력 할 때 출력 결 과 를 짧게 만 들 려 고 합 니 다.C 의 printf 나 cout 를 직접 사용 하여 double 수 를 인쇄 할 때 인쇄 결과 가 정밀도 가 있 는 결과 일 수도 있 습 니 다.16 진법 으로 더 정확 한 double 을 출력 할 수 있 습 니 다.

printf("%a
",d1);
출력 결과:

0x1.000000p+64
이 문제 1 은 사실상 C++에서 고정 밀 정 수 를 double 로 돌 릴 때의 짝수 반올림 문제 일 뿐이다.
문제 2 에 대해 서 는 float 나 double 에서 int 로 바 뀌 면 값 이 0 으로 반올림 됩 니 다.예 를 들 어 1.999 는 1 로 바 뀌 고-1.999 는-1 로 바 뀝 니 다.더 나 아가 값 이 넘 칠 수 있다.C 언어 표준 은 이러한 상황 에 대해 고정된 결 과 를 지적 하지 않 았 는데 이런 전환 행 위 는 정의 가 없다.
참조 링크:
[1] http://en.wikipedia.org/wiki/Double-precision_floating-point_format#cite_note-whyieee-1
[2]컴퓨터 시스템 을 깊이 이해 하고 Randal E.Bryant,기계공 업 출판사
[3]http://stackoverflow.com/questions/4738768/printing-double-without-losing-precision
C++에서 double 부동 소수점 정밀도 손실 에 대한 심도 있 는 분석 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 C+double 부동 소수점 정밀도 손실 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

좋은 웹페이지 즐겨찾기