C의 int 변수에 이중 정밀도를 할당하는 비직관적 결과
다음 코드에서 출력된 14번과 15번 두 개의 번호를 왜 구하는지 누가 설명 좀 해줄래?
#include <stdio.h>
int main()
{
double Vmax = 2.9;
double Vmin = 1.4;
double step = 0.1;
double a =(Vmax-Vmin)/step;
int b = (Vmax-Vmin)/step;
int c = a;
printf("%d %d",b,c); // 14 15, why?
return 0;
}
나는 두 경우 모두 15점을 받을 것으로 예상하지만 언어의 기초가 부족한 것 같다.
그게 관련이 있는지는 모르겠지만 코드블록에서 테스트를 하고 있었어.그러나 일부 온라인 컴파일러(예: 이 코드)에 동일한 코드 라인을 입력하면 두 개의 인쇄된 변수에 대해 15의 답이 나온다.
...왜 내가 두개의 다른 숫자를 가지게 되었는지...
일반적인 부동 소수점 문제는 제쳐두고 다음 연산 경로로 이동b
그리고c
다른 방법으로 도착했다. c
먼저 값을 다음과 같이 저장하여 계산한다.double a
.
double a =(Vmax-Vmin)/step;
int b = (Vmax-Vmin)/step;
int c = a;
C는 중간 부동소수점 산술을 더 넓은 유형을 사용하여 계산할 수 있도록 한다.값 의 확인FLT_EVAL_METHOD
로부터<float.h>
.
할당 및 캐스트(추가 범위와 정밀도를 모두 제거하는)를 제외하고...
-1 불변;
0은 유형의 범위와 정밀도에만 모든 연산 및 상수를 평가한다.
1의 및
float
그리고double
의 범위와 정밀도까지double
유형, 평가long double
의 범위와 정밀도에 대한 연산 및 상수long double
타자를 치다;2의 범위와 정밀도에 대한 모든 연산 및 상수를 평가한다.
long double
타자를 치다C11dr §5.2.4.2.2 9
OP 보고 2
에 대한 지수를 저장함으로써double a = (Vmax-Vmin)/step;
, 정밀도는 다음과 같이 강요된다.double
반면에int b = (Vmax-Vmin)/step;
라고 계산할 수 있다.long double
.
이 미묘한 차이는 다음과 같다.(Vmax-Vmin)/step
(아마도 있는)long double
되어 있다.double
남은 a에 대한 대long double
하나는 15(또는 바로 위), 다른 하나는 15 바로 밑이다. int
잘림 현상은 15와 14로 이 차이를 증폭시킨다.
다른 컴파일러의 경우, 두 결과가 모두 다음과 같은 이유로 동일했을 수 있다.FLT_EVAL_METHOD < 2
또는 기타 부동 소수점 특성.
전환int
부동 소수점부터 숫자가 정수에 가까울 정도로 심각하다.에게 종종 더 좋다.round()
또는lround()
가장 좋은 해결책은 상황에 따라 달라지는 것이다.
이것은 정말로 흥미로운 질문이다. 여기 당신의 하드웨어에서 정확히 무슨 일이 일어나는지 있다.의 정道를 한 것으로서 한국의 계을을을을을을한한한한 gives한한한한한한한한한한한 다.double
정밀 부동(예: 52비트 matissa + 하나의 암시적 비트)표현에 대한 자세한 내용은 위키백과 기사를 참조하십시오.
먼저 몇 가지 변수를 정의하십시오.
double Vmax = 2.9;
double Vmin = 1.4;
double step = 0.1;
2진수의 각 값은 다음과 같은 값이다.
Vmax = 10.111001100110011001100110011001100110011001100110011
Vmin = 1.0110011001100110011001100110011001100110011001100110
step = .00011001100110011001100110011001100110011001100110011010
비트를 세어보면 오른쪽에 52비트를 더한 첫 번째 비트를 내가 준 것을 알 수 있을 것이다.이것은 정확히 당신의 컴퓨터가 a를 저장하는 정밀도 입니다.double
. 값이 반올림되었다는 점에 유의하십시오.
이제 이 숫자들을 계산해봐첫 번째 수술인 뺄셈은 정확한 결과를 낳는다.
10.111001100110011001100110011001100110011001100110011
- 1.0110011001100110011001100110011001100110011001100110
--------------------------------------------------------
1.1000000000000000000000000000000000000000000000000000
그 다음 으로 나눈다.step
, 컴파일러에 의해 반올림된 것:
1.1000000000000000000000000000000000000000000000000000
/ .00011001100110011001100110011001100110011001100110011010
--------------------------------------------------------
1110.1111111111111111111111111111111111111111111111111100001111111111111
의 라운딩으로 인해step
, 결과는 약간 아래에 있다.15
이전과는 달리, 나는 즉시 라운딩을 하지 않았다. 왜냐하면 그곳이 바로 흥미로운 일이 일어나는 곳이기 때문이다.CPU는 실제로 a보다 더 큰 정밀도의 부동소수를 저장할 수 있다.double
, 그래서 라운딩은 즉시 일어나지 않는다.
그래서 결과를 변환할 때(Vmax-Vmin)/step
에 직접int
, 당신의 CPU는 단지 분절점 뒤의 비트를 잘라낸다 (이것이 암묵적인 방법이다).double -> int
변환은 언어 표준에 의해 정의된다.
1110.1111111111111111111111111111111111111111111111111100001111111111111
cutoff to int: 1110
그러나 결과를 이중 유형의 변수에 처음 저장하면 반올림이 수행된다.
1110.1111111111111111111111111111111111111111111111111100001111111111111
rounded: 1111.0000000000000000000000000000000000000000000000000
cutoff to int: 1111
그리고 이게 바로 네가 얻은 결과야.
"단순한" 대답은 겉보기에는 단순해 보이는 숫자 2.9, 1.4, 0.1은 모두 내부적으로 이진 부동소수로 표시되며, 이진에서는 숫자 1/10을 무한 반복하는 이진분수 0.0001100110011001100110011로 표시한다는 것이다.[2] . . (이것은 십진수에서 1/3이 0.3333333... . .) 다시 십진수로 환산하면, 그 원래의 숫자는 2.899999999999, 1.3999999999, 0.0999999999와 같은 것이 된다.그리고 그들에 대한 추가적인 계산을 할 때, 그 .099999999는 증식하는 경향이 있다.
그리고 또 다른 문제는 어떤 것을 계산하는 경로 -- 특정 유형의 중간 변수에 저장하든, 아니면 "모두 한 번에" 계산하든 간에, 프로세서가 유형보다 더 정밀하게 내부 레지스터를 사용할 수 있다는 겁니다.double
큰 수 있다 - 안 된다.
요컨대, 변환할 때double
로 되돌아가다.int
너는 거의 항상 자르기가 아니라 둥글게 하고 싶어 한다.여기서 일어난 일은 (실제로) 하나의 계산 경로가 15.0000000001을 줬고, 다른 하나는 14.9999999999를 줬다는 겁니다.
FLT_EQUAL_METHOD=2에 대한 C 프로그램의 분석에서 등가 문제를 분석한다.
만약FLT_EVAL_METHOD==2
:
double a =(Vmax-Vmin)/step;
int b = (Vmax-Vmin)/step;
int c = a;
계산하다b
A를 평가하여long double
식을 사용하여 이를 잘라내십시오.int
에 ,,,,라면.c
로부터 평가하고 있다.long double
, 잘라내기 위치double
그 다음으로는int
.
따라서 두 값 모두 동일한 공정을 통해 얻을 수 없으며, 부동형은 통상적인 정확한 산수를 제공하지 않기 때문에 다른 결과를 초래할 수 있다.
'Programing' 카테고리의 다른 글
Google 로그인 사용 오류 - vue "gapi가 정의되지 않음" (0) | 2022.04.17 |
---|---|
Java에서 Kotlin 확장 기능에 액세스 (0) | 2022.04.17 |
Vue.js - 요소 UI - 폼 유효성 검사 상태를 확인하는 방법 (0) | 2022.04.17 |
무료()가 내 어레이의 길이를 알고 있다면 왜 내 코드로 요청할 수 없는가? (0) | 2022.04.17 |
Java 클라이언트에서 서버의 자체 서명 SSL 인증서 수락 (0) | 2022.04.17 |