배정 밀도와 단 정밀도의 차이점에 대해 읽었습니다. 그러나 대부분의 경우, float
그리고 double
하나를 사용하여 즉, 상호 교환 보이거나 다른 하나는 결과에 영향을 미칠 것 같지 않습니다. 이것이 사실입니까? 수레와 복식은 언제 교환 할 수 있습니까? 그들 사이의 차이점은 무엇입니까?
배정 밀도와 단 정밀도의 차이점에 대해 읽었습니다. 그러나 대부분의 경우, float
그리고 double
하나를 사용하여 즉, 상호 교환 보이거나 다른 하나는 결과에 영향을 미칠 것 같지 않습니다. 이것이 사실입니까? 수레와 복식은 언제 교환 할 수 있습니까? 그들 사이의 차이점은 무엇입니까?
답변:
큰 차이.
이름에서 알 수 있듯이 a double
의 정밀도는 [1] 의 2 배 입니다. 일반적으로 a 는 십진수 15 자리이며, 7은 7입니다.float
double
float
자릿수 계산 방법은 다음과 같습니다.
double
52 가수 비트 + 1 숨겨진 비트 : log (2 53 ÷÷ log (10) = 15.95 자리
float
23 개의 가수 비트 + 1 개의 숨겨진 비트 : log (2 24 ÷÷ log (10) = 7.22 자리
이 정밀 손실로 인해 반복 계산을 수행 할 때 더 큰 잘림 오류가 누적 될 수 있습니다.
float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.7g\n", b); // prints 9.000023
동안
double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.15g\n", b); // prints 8.99999999999996
또한 float의 최대 값은 약 3e38
이지만 double은 약 1.7e308
이므로를 사용 float
하면 "인피니티"(예 : 특수 부동 소수점 숫자)를 double
간단한 것보다 훨씬 쉽게 맞출 수 있습니다 (예 : 계승 60 계산).
테스트하는 동안 소수의 테스트 사례에 이러한 큰 숫자가 포함되어있을 수 있으며 플로트를 사용하면 프로그램이 실패 할 수 있습니다.
물론 때로는 double
충분히 정확 하지도 않기 때문에 때때로 우리는 long double
[1] (위의 예는 Mac에서 9.000000000000000066을 나타냄)을 갖지만 모든 부동 소수점 유형은 반올림 오류를 겪 습니다 . 따라서 정밀도가 매우 중요한 경우 (예 : 돈) 처리) int
또는 분수 클래스 를 사용해야합니다 .
또한 +=
오류가 빠르게 누적되므로 많은 부동 소수점 수를 합산 하는 데 사용하지 마십시오 . Python을 사용하는 경우을 사용하십시오 fsum
. 그렇지 않으면 Kahan summation 알고리즘 을 구현하십시오 .
[1] : C 및 C ++ 표준은 float
, double
및 의 표현을 지정하지 않습니다 long double
. 세 가지 모두 IEEE 배정 밀도로 구현 될 수 있습니다. 그럼에도 불구하고, 대부분의 아키텍처 (GCC, MSVC, 86, 64, ARM은) float
입니다 실제로 소수점 수 (binary32)를 부동 IEEE 단 정밀도하고 double
있다 IEEE 배정 밀도 부동 소수점 숫자 (binary64).
표준 C99 (ISO-IEC 9899 6.2.5 §10) 또는 C ++ 2003 (ISO-IEC 14882-2003 3.1.9 §8) 표준은 다음과 같습니다.
부동 소수점 유형
float
에는double
, 및 , 세 가지가 있습니다long double
. 타입double
은 최소한 정밀도를 제공float
하고 타입long double
은 최소한 정밀도를 제공double
합니다. 유형의 값 세트는 유형의 값 세트의float
서브 세트입니다double
. 유형의 값 세트는 유형의 값 세트의double
서브 세트입니다long double
.
C ++ 표준은 다음을 추가합니다.
부동 소수점 유형의 값 표현은 구현 정의됩니다.
IEEE 부동 소수점 표준을 심도있게 다루는 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 훌륭한 점을 살펴볼 것을 제안 합니다. 표현 세부 사항에 대해 배우고 크기와 정밀도 사이에 상충 관계가 있음을 알게됩니다. 부동 소수점 표현의 정밀도는 크기가 감소함에 따라 증가하므로 -1과 1 사이의 부동 소수점 숫자는 가장 정밀한 숫자입니다.
이차 방정식을 감안할 : X 2 - 4.0000000 X + 3.9999999 = 0, 정확한 뿌리 10의 유효 숫자는, R 1 = 2.000316228 및 R 2 = 1.999683772.
float
and를 사용 double
하여 테스트 프로그램을 작성할 수 있습니다.
#include <stdio.h>
#include <math.h>
void dbl_solve(double a, double b, double c)
{
double d = b*b - 4.0*a*c;
double sd = sqrt(d);
double r1 = (-b + sd) / (2.0*a);
double r2 = (-b - sd) / (2.0*a);
printf("%.5f\t%.5f\n", r1, r2);
}
void flt_solve(float a, float b, float c)
{
float d = b*b - 4.0f*a*c;
float sd = sqrtf(d);
float r1 = (-b + sd) / (2.0f*a);
float r2 = (-b - sd) / (2.0f*a);
printf("%.5f\t%.5f\n", r1, r2);
}
int main(void)
{
float fa = 1.0f;
float fb = -4.0000000f;
float fc = 3.9999999f;
double da = 1.0;
double db = -4.0000000;
double dc = 3.9999999;
flt_solve(fa, fb, fc);
dbl_solve(da, db, dc);
return 0;
}
프로그램을 실행하면 다음이 제공됩니다.
2.00000 2.00000
2.00032 1.99968
숫자는 크지 않지만 여전히을 사용하여 취소 효과를 얻을 수 float
있습니다.
(실제로 위의 방법은 단 정밀도 또는 배정 밀도 부동 소수점 숫자를 사용하여 2 차 방정식을 푸는 가장 좋은 방법은 아니지만 보다 안정적인 방법을 사용하더라도 답은 변하지 않습니다 .)
32 비트 길이의 float 유형은 정밀도가 7 자리입니다. 매우 크거나 아주 작은 범위 (+/- 3.4 * 10 ^ 38 또는 * 10 ^ -38)의 값을 저장할 수 있지만 유효 숫자는 7 자리입니다.
64 비트 길이의 double 유형은 더 큰 범위 (* 10 ^ + /-308)와 15 자리 정밀도를 갖습니다.
long double 타입은 명목상 80 비트이지만 주어진 컴파일러 / OS 쌍은 정렬을 위해 12-16 바이트로 저장할 수 있습니다. long double에는 엄청나게 큰 지수가 있으며 19 자리의 정밀도를 가져야합니다. Microsoft는 무한한 지혜로 long double을 8 바이트로 제한하며 일반 double과 동일합니다.
일반적으로 부동 소수점 값 / 변수가 필요할 때 double 유형을 사용하십시오. 표현식에 사용 된 리터럴 부동 소수점 값은 기본적으로 더블로 처리되며 부동 소수점 값을 반환하는 대부분의 수학 함수는 더블을 반환합니다. 두 번만 사용하면 많은 두통과 타입 캐스팅을 줄일 수 있습니다.
방금 나를 알아 내고 실수로 플로트 정밀도의 좋은 예를 줄 수있는 오류가 발생했습니다.
#include <iostream>
#include <iomanip>
int main(){
for(float t=0;t<1;t+=0.01){
std::cout << std::fixed << std::setprecision(6) << t << std::endl;
}
}
출력은
0.000000
0.010000
0.020000
0.030000
0.040000
0.050000
0.060000
0.070000
0.080000
0.090000
0.100000
0.110000
0.120000
0.130000
0.140000
0.150000
0.160000
0.170000
0.180000
0.190000
0.200000
0.210000
0.220000
0.230000
0.240000
0.250000
0.260000
0.270000
0.280000
0.290000
0.300000
0.310000
0.320000
0.330000
0.340000
0.350000
0.360000
0.370000
0.380000
0.390000
0.400000
0.410000
0.420000
0.430000
0.440000
0.450000
0.460000
0.470000
0.480000
0.490000
0.500000
0.510000
0.520000
0.530000
0.540000
0.550000
0.560000
0.570000
0.580000
0.590000
0.600000
0.610000
0.620000
0.630000
0.640000
0.650000
0.660000
0.670000
0.680000
0.690000
0.700000
0.710000
0.720000
0.730000
0.740000
0.750000
0.760000
0.770000
0.780000
0.790000
0.800000
0.810000
0.820000
0.830000
0.839999
0.849999
0.859999
0.869999
0.879999
0.889999
0.899999
0.909999
0.919999
0.929999
0.939999
0.949999
0.959999
0.969999
0.979999
0.989999
0.999999
0.83 이후에 볼 수 있듯이 정밀도는 크게 떨어집니다.
그러나 t
이중으로 설정하면 이러한 문제가 발생하지 않습니다.
이 사소한 오류를 깨닫는 데 5 시간이 걸렸으며 이로 인해 프로그램이 손상되었습니다.
double
하는 것은 좋은 해결책이 아닙니다. int
부동 소수점 값을 얻기 위해 내부 곱셈을 계산하고 수행하는 데 사용 합니다.
플로트는 복식보다 정밀도가 떨어집니다. 이미 알고 있지만 더 나은 이해를 위해 부동 소수점 산술에 대해 알아야 할 내용을 읽으십시오 .
부동 소수점 숫자를 사용할 때는 로컬 테스트가 서버 측에서 수행 된 테스트와 정확히 동일하다는 것을 신뢰할 수 없습니다. 환경과 컴파일러는 로컬 시스템과 최종 테스트가 실행되는 위치가 다를 수 있습니다. 일부 TopCoder 경쟁 에서이 문제를 여러 번 전에 보았습니다. 특히 두 개의 부동 소수점 숫자를 비교하려고하면 더욱 그렇습니다.