2017-12-21 41 views
4

부동 소수점과 관련된 문제를 완전히 이해하고 있지만 설명 할 수없는 매우 흥미로운 동작을 보았습니다.부동 소수점의 부정확성 피하기

float x = 1028.25478; 
long int y = 102825478; 
float z = y/(float)100000.0; 
printf("x = %f ", x); 
printf("z = %f",z); 

출력은 :

X = 1028.254761 Z = 지금 1028.254780

부동 숫자 I는 변수 x에 해당 할당 할 때 특정 랜덤 값 (1028.25478)을 나타내는 데 실패하면 . 변수 z의 경우 왜 동일하지 않습니까?

P. 나는 pellesC IDE를 사용하여 코드 (C11 컴파일러)를 테스트하고있다.

+0

귀하의 코드 [내게 똑같은 것이 인쇄됩니다] (https://ideone.com/9zyYyF). – dasblinkenlight

+0

예를 들어 몇 자루 더 인쇄하십시오. ' "% .10f"'. –

+0

이것은 복제품이 아닙니다. 나는 몇 가지 실제 중복이 있다고 생각하지만, 찾기가 어렵다. 기본적으로 컴파일러는 두 번째 경우에 'double'으로 수학 연산을 수행합니다. – dasblinkenlight

답변

5

여기서 일어나는 일은 후자의 부동 소수점 변수가 생략되어 double-precision 레지스터에 보관된다는 것입니다. 그 다음에 그대로 printf의 인수로 전달됩니다. 그런 다음 컴파일러는 기본 인수 판촉 후 배정도로이 숫자를 전달하는 것이 안전하다고 생각합니다.

는 I 이러한 스위치, GCC 7.2.0 사용 유사한 결과를 생성하는 것을 처리 :

-Wall -Werror -ffast-math -m32 -funsafe-math-optimizations -fexcess-precision=fast -O3 

출력

x = 1028.254761 z = 1028.254800 

개수가 약간 차이가있다.^

description for -fexcess-precision=fast는 말한다 :

-fexcess-precision=style

이 옵션은 부동 소수점 연산이 더 정밀도 또는 범위 IEEE 표준보다 더와 가진 형식에서 발생 시스템에 과도한 정밀도를 통해 더 제어 할 수 있습니다 교환 부동 소수점 유형. 기본적으로 -fexcess-precision=fast은 효과에 있습니다. 즉, 정밀도가 소스 코드에 지정된 유형보다 더 광범위하게 수행 될 수 있으며 이는 이 더 빠른 코드가되고 으로 반올림하면 소스 코드에 지정된 유형이 반올림 될 때 예측할 수 없음을 의미합니다. C를 컴파일 할 때 -fexcess-precision=standard이 지정되면 초과 정밀도는 ISO C99에 지정된 규칙을 따릅니다. 에서 특히 캐스트와 할당 모두 을 의미 유형으로 반올림합니다 (반면 -ffloat-store은 할당에만 영향을줍니다). -std=c99과 같은 엄격한 준수 옵션을 사용하는 경우이 옵션 [-fexcess-precision=standard]은 C에 대해 기본적으로 사용됩니다. -ffast-math 은 의 엄격한 준수 옵션 사용 여부에 관계없이 기본적으로 -fexcess-precision=fast을 사용합니다.

이 문제가 IEEE754 엄격한 부동 소수점이 제한

+0

저에게는 많은 의미가 있지만, C11이라고 확신합니다. –

+0

또한 CCFlags는 다음과 같습니다. -std : C11 -Tx86-coff -Ot -Ob1 -fp : precise -W1 -Gd –

+0

예, 잘못된 컴파일러의 경우 여야합니다. Pelle 's에서 * assembler output *을 얻을 수 있습니까? –

2

C11 호환되지 않습니다, 답변은 동일해야합니다.

1028.25478은 실제로 1028.2547607421875입니다. 그 계정은 x입니다.

y/(float)100000.0;의 평가에서, y은 C의 인수 촉진 규칙에 따라 float으로 변환됩니다. 가장 가까운 float ~ 102825478102825480입니다. IEEE754는 가장 좋은 결과를 1028.2547607421875 (z의 값)이어야합니다. 가장 가까운 숫자는 1028.25480입니다.

내 대답은 관찰 된 행동과 다릅니다. 나는 플로팅 포인트를 엄격하게 구현하지 않는 컴파일러에 대해 설명했다. IEEE754를 구현하지 않았을 수도 있습니다.

+1

물론 C는 IEEE-754를 엄격히 준수하거나 구현하지 않아도됩니다. 실제로, 현대의 범용 플랫폼에 대한 모든 구현은 실제로 IEEE-754 형식과 적어도 IEEE-754 의미를 사용합니다. 적어도 기본적으로 IEEE-754를 엄격히 준수하지는 않습니다. –

2

코드는 zdouble이고 y/(float)100000.0y/100000.0 인 것처럼 동작합니다.

float x = 1028.25478; 
long int y = 102825478; 
double z = y/100000.0; 

// output 
x = 1028.254761 z = 1028.254780 

중요한 고려 사항은 FLT_EVAL_METHOD입니다. 이렇게하면 선택 부동 소수점 코드가 더 높은 정밀도로 평가됩니다.

#include <float.h> 
#include <stdio.h> 
printf("FLT_EVAL_METHOD %d\n", FLT_EVAL_METHOD); 
할당 제외

와 ..., 일반적인 산술 변환 및 부동 상수의 대상이 부동 피연산자 운영자에 의해 산출 값과 값은 그 범위와 정밀도 수있는 형식으로 평가된다 캐스트 유형에 필요한 것보다 커야합니다. 평가 포맷의 사용은 구현 정의 값인 FLT_EVAL_METHOD을 특징으로한다.

-1 불확정;
0은 유형의 범위와 정밀도에 대한 모든 연산과 상수를 평가합니다.
1는 ... 범위와 double 타입의 정밀도 및 유형 floatdouble 평가 long double 유형의 범위 및 정밀도 ... long double 평가;
2 모든 범위를 ... long double 유형의 범위와 정밀도로 평가합니다.

는 그러나이 float z = y/(float)100000.0;z가 할당에있는 모든 높은 정밀도를 분실로 하지 적용한다.


는 그 코드가 부동 소수점 연산의 예상 규칙에 덜 준수가 속도 최적화를 사용 @Antti Haapala에 동의합니다.

관련 문제