C 언어 에서 마 성의 float 부동 소수점 정밀도 문제

한 문제 에서 끌어들이다
만약 당신 이 이전에 C 언어 를 접 한 적 이 있다 면 아래 의 이 코드 에 대해 잘 알 것 입 니 다.

#include <stdio.h>

int main(void)
{
    float f_num1 = 21.75;
    float f_num2 = 13.45;
    printf("f_num1 = %f
", f_num1); printf("f_num2 = %f
", f_num2); printf("f_num1 + f_num2 = %f
", f_num1 + f_num2); return 0; }
많은 사람들 이 운행 하지 않 고 바로 답 을 제시 할 수 있 을 거 라 고 믿 습 니 다.fnum1 = 21.75 , f_num2 = 13.45 , f_num1 + f_num 2=35.2 는 상식 적 으로 나 이론 적 으로 나 이해 하기 어렵 지 않다.
다음은 우리 가 프로그램 을 실행 해서 우리 의 추측 이 정확 하지 않 은 지 검증 합 니 다.
f_num1 = 21.750000
f_num2 = 13.450000
f_num1 + f_num2 = 35.200001
f_num 1 과 fnum2 의 결 과 는 우리 가 예 상 했 던 것 과 마찬가지 로 뒤에 0 이 4 개 더 생 긴 이 유 는%f 가 기본적으로 6 자리 유효 숫자 를 유지 하기 때 문 입 니 다.하지만 fnum1 + f_num 2 의 결 과 는 무엇 입 니까?이 35.20001 은 어디에서 왔 습 니까?
우리 의 인식 을 단번에 뒤 집 은 것 이 아 닙 니까?
놀 라 지 않 으 면 놀 라 지 않 고,뜻 이 의외 이지 않 으 면 자극 적 이지 않 습 니까?C 언어 를 배 운 후부 터 간단 한 산술 도 할 줄 모 르 는 것 을 발 견 했 습 니까?
서 두 르 지 마라,그리고 너 를 더욱 붕괴 시 킬 것 이다.
C++라면?
다음은 위의 프로그램의 C++버 전 을 살 펴 보 겠 습 니 다.

#include<iostream>
using namespace std;

int main(void)
{
    float f_num1 = 21.75;
    float f_num2 = 13.45;
    cout << "f_num1 = " << f_num1 << endl;
    cout << "f_num2 = " << f_num2 << endl;
    cout << "f_num1 + f_num2 = " << f_num1 + f_num2 << endl;
    return 0;
}

출력 결 과 를 직접 보 세 요:
f_num1 = 21.75
f_num2 = 13.45
f_num1 + f_num2 = 35.2
신기 하 죠?그 결과 가 훨씬 정상 으로 보 였 기 때문이다.
여기 서 우리 의 마음 속 에 큰 의문 이 있다 고 믿 습 니 다.왜 C 프로그램 과 C+프로그램 이 같은 숫자 에 대해 처리 하고 출력 결 과 는 다 릅 니까?cout 는 도대체 무엇 을 했 습 니까?
cout 의 신기 한 점
cout 가 부동 소수점 에 대한 처 리 를 검증 하기 위해 서 다음 절 차 를 살 펴 보 자.

#include <iostream>
using namespace std;

int main(void)
{
    float num1 = 5;
    float num2 = 5.00;
    float num3 = 5.14;
    float num4 = 5.140000;
    float num5 = 5.123456;
    float num6 = 5.987654321;
    cout << "num1 = " << num1 << endl;
    cout << "num2 = " << num2 << endl;
    cout << "num3 = " << num3 << endl;
    cout << "num4 = " << num4 << endl;
    cout << "num5 = " << num5 << endl;
    cout << "num6 = " << num6 << endl;

    return 0;
}
결 과 를 보면 비교적 직관 적 이 고 상기 절 차 를 실행 한 결 과 는 다음 과 같다.
num1 = 5
num2 = 5
num3 = 5.14
num4 = 5.14
num5 = 5.12346
num6 = 5.98765
num 1 과 num 2,num 3 과 num 4 두 조 의 결 과 를 통 해 알 수 있 듯 이 cout 는 float 유형 수치 소수점 뒤의 0 을 직접 절약 한 것 입 니 다(이 점 은 C 언어 포맷 출력%g 과 비슷 합 니 다).
num 5 와 num 6 두 그룹의 결 과 를 분석 해 보면 cout 는 부동 소수점 수치 에 대해 최대 6 비트 의 유효 숫자 를 보류한다.
이상 은 cout 가 부동 소수점 을 처리 할 때의 특징 이 므 로 기억 해 야 합 니 다.
사실,우 리 는 iostream 라 이브 러 리 의 cout.setf 를 사용 하여 cout 의 정밀 도 를 회복 하 는 것 은 어렵 지 않다.우 리 는 위의 코드 를 다음 과 같이 수정 합 니 다.

#include<iostream>
using namespace std;

int main(void)
{
    float f_num1 = 21.75;
    float f_num2 = 13.45;
    cout.setf(ios_base::fixed, ios_base::floatfield);    
    cout << "f_num1 = " << f_num1 << endl;
    cout << "f_num2 = " << f_num2 << endl;
    cout << "f_num1 + f_num2 = " << f_num1 + f_num2 << endl;
    return 0;
}

출력 결 과 는 C 언어 버 전과 똑 같 습 니 다.
f_num1 = 21.750000
f_num2 = 13.450000
f_num1 + f_num2 = 35.200001
답 이 나 오고 싶다.
글 을 여기까지 썼 으 니,네가 이미 문제 의 소 재 를 알 아 냈 을 것 이 라 고 믿는다.
좋아,결과 가 다른 이 유 는 바로 정밀도 때 문 이 야!
공식 교재 에서 float 정밀도 에 대한 설명 을 살 펴 보 자.
부동 소수점 형 과 단일 정밀도,이중 정밀도 와 확장 정밀도 값 을 나타 낸다.C++표준 은 부동 소수점 유효 자릿수 의 최소 값 을 지정 하 였 으 나 대부분의 컴 파일 러 는 더욱 높 은 정 도 를 실현 하 였 다.보통 float 는 한 글자(32 비트)로 표시 되 며,double 은 2 글자(64 비트)로 표시 되 며,long double 은 3 또는 4 글자(96 또는 128 비트)로 표시 된다.일반적으로 유형 float 와 double 은 각각 7 과 16 개의 유효 위치 가 있다.유형 long double 은 특수 한 부동 소수점 수요 가 있 는 하드웨어 에 자주 사용 되 는데 구체 적 인 실현 이 다 르 고 정밀도 도 각각 다르다.C++Primer 제5 판)
상기 설명 에서 우 리 는 float 에 있어 최대 7 개의 유효 위치 만 있다 는 것 을 알 기 어렵 지 않다.이것 은 실제 저장 의 정밀도 가 float 의 정밀도 범위 보다 크 면 정밀도 손실 현상 이 나타 난 다 는 것 을 의미한다.
상술 한 문 제 를 더욱 증명 하기 위해 서,우 리 는 float 의 수 치 를 10 억 배로 확대 하여,안에 저 장 된 값 이 도대체 얼마나 되 는 지 보 는 것 도 좋 습 니 다.

#include<iostream>
using namespace std;

int main(void)
{
    float f_num1 = 21.75;
    float f_num2 = 13.45;
    cout.setf(ios_base::fixed, ios_base::floatfield);
    int billion = 1E9;
    float f_num10 = f_num1 * billion;
    float f_num20 = f_num2 * billion;
    cout << "f_num1 = " << f_num1 << endl;
    cout << "f_num2 = " << f_num2 << endl;

    cout << "f_num10 = " << f_num10 << endl;
    cout << "f_num20 = " << f_num20 << endl;
    return 0;
}

위의 프로그램 실행 결 과 는 다음 과 같 습 니 다.
f_num1 = 21.750000
f_num2 = 13.450000
f_num10 = 21749999616.000000
f_num20 = 13449999360.000000
이 를 통 해 우 리 는 21.75 가 실제 저장 할 때 저 장 된 21.75 가 아니 라 21.74999616 이 라 고 추측 하기 어렵 지 않다.마찬가지 로 12.45 에 저 장 된 것 은 12.449999360 이다.이렇게 계산 하면 자연히 결과 가 정확 하지 않 을 것 이다.
예 를 하나 더 보다.
우 리 는 정밀도 손실 로 인해 연산 결과 가 정확 하지 않 은 예 를 다시 보 았 다.

#include<iostream>
using namespace std;

int main(void)
{
    float num1 = 2.3410E23;
    float num2 = num1 + 1.0f;
    cout << "num2 - num1 = " << num2 - num1 << endl;
    return 0;
}

정밀도 가 잃 어 버 리 지 않 으 면 연산 결 과 는 1 이 어야 하 는데 정밀도 가 잃 어 버 려 서 마지막 에 1 을 더 하 는 것 은 실제 효과 가 없 는 것 과 마찬가지 로 계 산 된 결 과 는 0 이다.
num2 - num1 = 0
어떻게
그렇다면 플 로 트 에 게 이렇게 기괴 한 문제 가 많 으 니 어떻게 해결 하고 피해 야 할 까?
우선,물론 프로 그래 밍 할 때 높 은 정밀도 의 부동 소수점 유형 을 사용 하 는 것 을 추천 합 니 다.
예 를 들 어 double 은 float 보다 정밀도 가 높 고 double 을 사용 하면 많은 문 제 를 피 할 수 있다.예 를 들 어 본 고 에서 처음에 언급 한 문 제 는 double 을 사용 하면 완벽 하 게 해결 할 수 있다.

#include <stdio.h>

int main(void)
{
    double f_num1 = 21.75;
    double f_num2 = 13.45;
    printf("f_num1 = %lf
", f_num1); printf("f_num2 = %lf
", f_num2); printf("f_num1 + f_num2 = %lf
", f_num1 + f_num2); return 0; }
여러분 스스로 운행 해서 결 과 를 볼 수 있 습 니 다.
더 블 유형 은 대부분의 정밀도 손실 문 제 를 해결 할 수 있 고 기본적으로 일상적인 사용 을 만족 시 킬 수 있 지만 정밀도 손실(더 블 도 정밀도 제한 이 있다)을 피 할 수 없다.이 럴 때 는 다른 방법 을 생각해 서 해결 해 야 한다.
만능 cout
앞에서 언급 했 듯 이 cout 는 이러한 정밀도 손실 문 제 를 해결 할 수 있 습 니 다.따라서 효율 에 대한 요구 가 너무 높 거나 포맷 출력 을 요구 하지 않 는 다 면(사실 cout 도 포맷 출력 을 실현 할 수 있 습 니 다.여기 서 자세히 전개 되 지 않 습 니 다)printf 를 사용 해 야 합 니 다.C+프로그램 을 작성 할 때 printf 대신 cout 를 사용 하 는 것 을 권장 합 니 다.
마지막 에 쓰다
본 고 는 단지 부동 소수점 수치의 정밀도 문 제 를 간단하게 소개 했다.만약 에 깊이 연구 하려 면 이렇게 많은 내용 이 아 닐 것 이다.예 를 들 어 부동 소수점 수 치 는 메모리 에 어떻게 저장 되 었 습 니까?바이트 안에 어떻게 분포 합 니까?이것 이 야 말로 진정한 핵심 원리 부분 이다.여기 서 는 수박 겉 핥 기 식 으로 만 이 야 기 했 지만 읽 는 사람 이 정밀도 문제 에 대해 초보적인 인식 을 가지 고 있다 고 믿는다.
C 언어 중 마 성의 플로트 부동 소수점 정밀도 문제 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 C 언어 플로트 부동 소수점 정밀도 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

좋은 웹페이지 즐겨찾기