float 리터럴을 사용한 float 비교의 이상한 출력
float f = 0.7;
if( f == 0.7 )
printf("equal");
else
printf("not equal");
출력이 표시되는 이유not equal?
왜 이런 일이 일어날까요?
이것은 당신의 진술에서 발생합니다.
if(f == 0.7)
0.7은 이중으로 처리됩니다.0.7f를 사용하여 값이 플로트로 처리되는지 확인합니다.
if(f == 0.7f)
그러나 Michael이 아래 의견에서 제안한 것처럼 부동 소수점 값의 정확한 동일성을 테스트해서는 안 됩니다.
이 답은 기존의 답을 보완하는 것입니다. 0.7은 부동 소수점(또는 이중점)으로 정확하게 표현할 수 없습니다.만약 정확하게 표현되었다면, 플로트로 변환했다가 다시 더블로 변환할 때 정보 손실이 없을 것이고, 이런 문제는 없을 것입니다.
특히 표준이 너무 모호할 때, 그 시간으로 설정된 모드에서 실행 시간에 반올림이 이루어질 것인지 아니면 다른 반올림 모드에서 컴파일 시간에 반올림이 이루어질 것인지에 대해 정확하게 표현할 수 없는 리터럴 부동 소수점 상수에 대한 컴파일러 경고가 있어야 한다고 주장할 수 있습니다.
정확하게 표현할 수 있는 모든 정수가 아닌 숫자는 다음과 같습니다.5그들의 마지막 십진수로. 그 : 어떤 은 불하게도사, 그는실아닙다니: 숫는자일부이반을 가지고 있습니다.5정확하게 표현할 수 없는 마지막 십진수입니다.작은 정수는 모두 정확하게 표현할 수 있으며, 2의 거듭제곱은 정규화되지 않은 숫자의 영역을 입력하지 않는 한 표현할 수 있는 다른 숫자로 변환합니다.
우선 플로트 번호 내부를 살펴보도록 하겠습니다. 0 32이고, 는 길가 4바이 (2진 32), 0.1을다. 16진수입니다.
3D CC CC CD.
하려면 다음과 같이 . IEEE 754 표진준따 10수면변려다음같이합니다해야과환하라에로▁by다합.
는 "3D CC CD"입니다.
0 01111011 1001100 11001100 11001101
여기서 첫 번째 숫자는 부호 비트입니다.0은 (-1)^0을 의미합니다.
두 번째 8비트는 지수입니다.이진수에서는 01111011 - 소수 123입니다.그러나 실제 지수는 123-127(항상 127)=-4입니다. 즉, 얻을 수 있는 숫자에 2^(-4)을 곱해야 합니다.
마지막 23바이트는 유의 및 정밀도입니다.여기서 첫 번째 비트는 1/(2^1)(0.5), 두 번째 비트는 1/(2^2)(0.25)로 곱합니다.다음과 같은 정보를 얻을 수 있습니다.
우리는 모든 숫자(2의 거듭제곱)를 더하고 거기에 1(항상 1, 스탠다드아트 기준)을 더해야 합니다.그렇다.
1,60000002384185791015625
이제 이 숫자에 2^(-4)를 곱해 보겠습니다. 지수입니다.로 4번만 : 위의로숫 2회 4만더 4하면 니됩다 자를됩 다니▁we.
0,100000001490116119384765625
는 MS 했습니다.
**
이제 두 번째 파트.10진수에서 2진수로 변환합니다.
**
저는 0.1이라는 숫자를 택합니다.
정수 부분이 없기 때문에 쉽습니다.bit - 첫 번부 비트입 - .지수와 유의 및 정밀도를 계산합니다.논리에 2 정수(0.1*2=0.2)를 곱하고 1을 초과하면 계속합니다.
그리고 번호는 .00011001100110011001100110011입니다. 스탠다트는 우리가 1을 얻기 전에 왼쪽으로 이동해야 한다고 말합니다. (뭔가).보시는 바와 같이 지수(127-4=123)를 계산하는 이 숫자에서 4교대가 필요합니다.그리고 지금의 중요하고 정확한 것은
1001100110011001100110011001100(및 손실된 비트가 있습니다).
이제 전체 숫자입니다.부호 비트 0 지수는 123(01111011)이고, 유의 및 정밀도는 1001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100은 전체입니다.
00111101110011001100110011001100110011001100은 이전 챕터의 것과 비교해 보겠습니다.
00111101110011001100110011001101
보시다시피 마지막 비트는 같지 않습니다.그것은 제가 숫자를 깎기 때문입니다.CPU와 컴파일러는 Significant 및 Precision이 유지할 수 없고 마지막 비트를 1로 설정한 이후의 것임을 알고 있습니다.
거의 정확한 또 다른 질문은 이 질문과 연결되어 있습니다. 따라서 수년간 늦은 답변입니다.저는 위의 답변들이 완전하지 않다고 생각합니다.
int fun1 ( void )
{
float x=0.7;
if(x==0.7) return(1);
else return(0);
}
int fun2 ( void )
{
float x=1.1;
if(x==1.1) return(1);
else return(0);
}
int fun3 ( void )
{
float x=1.0;
if(x==1.0) return(1);
else return(0);
}
int fun4 ( void )
{
float x=0.0;
if(x==0.0) return(1);
else return(0);
}
int fun5 ( void )
{
float x=0.7;
if(x==0.7f) return(1);
else return(0);
}
float fun10 ( void )
{
return(0.7);
}
double fun11 ( void )
{
return(0.7);
}
float fun12 ( void )
{
return(1.0);
}
double fun13 ( void )
{
return(1.0);
}
Disassembly of section .text:
00000000 <fun1>:
0: e3a00000 mov r0, #0
4: e12fff1e bx lr
00000008 <fun2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
00000010 <fun3>:
10: e3a00001 mov r0, #1
14: e12fff1e bx lr
00000018 <fun4>:
18: e3a00001 mov r0, #1
1c: e12fff1e bx lr
00000020 <fun5>:
20: e3a00001 mov r0, #1
24: e12fff1e bx lr
00000028 <fun10>:
28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8>
2c: e12fff1e bx lr
30: 3f333333 svccc 0x00333333
00000034 <fun11>:
34: e28f1004 add r1, pc, #4
38: e8910003 ldm r1, {r0, r1}
3c: e12fff1e bx lr
40: 66666666 strbtvs r6, [r6], -r6, ror #12
44: 3fe66666 svccc 0x00e66666
00000048 <fun12>:
48: e3a005fe mov r0, #1065353216 ; 0x3f800000
4c: e12fff1e bx lr
00000050 <fun13>:
50: e3a00000 mov r0, #0
54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc>
58: e12fff1e bx lr
5c: 3ff00000 svccc 0x00f00000 ; IMB
왜 fun3와 fun4는 하나만 돌려주고 나머지는 돌려주지 않았나요?fun5는 왜 작동합니까?
그것은 언어에 관한 것입니다.언어는 0.7이 이 구문을 사용하지 않는 한 0.7은 2배라고 합니다. 그래서 0.7은 단일입니다.
float x=0.7;
이중 0.7은 단일으로 변환되어 x에 저장됩니다.
if(x==0.7) return(1);
언어는 우리가 더 높은 정밀도로 홍보해야 한다고 말합니다. 그래서 단일 인덱스 x는 더블로 변환되고 더블 0.7과 비교됩니다.
00000028 <fun10>:
28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8>
2c: e12fff1e bx lr
30: 3f333333 svccc 0x00333333
00000034 <fun11>:
34: e28f1004 add r1, pc, #4
38: e8910003 ldm r1, {r0, r1}
3c: e12fff1e bx lr
40: 66666666 strbtvs r6, [r6], -r6, ror #12
44: 3fe66666 svccc 0x00e66666
싱글 3f333333 더블 3fe6666666666
Alexandr가 지적했듯이, 만약 그 답이 IEEE 754로 유지된다면 싱글은
보기 effffffffffffffffffffff.
그리고 이중은
참고로 보기 effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.
23비트의 분수가 아닌 52비트의 분수를 가지고 있습니다.
00111111001100110011... single
001111111110011001100110... double
0 01111110 01100110011... single
0 01111111110 01100110011... double
10번 베이스의 3분의 1이 0.3333333인 것처럼...영원히.여기 반복되는 패턴이 있습니다. 0110
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
그리고 여기 답이 있습니다.
if(x==0.7) return(1);
x는 011001100110011001100110011을 분수로 포함하며, 그것이 다시 두 배로 변환될 때 분수는 다음과 같습니다.
01100110011001100110011000000000....
와 동등하지 않은
01100110011001100110011001100110...
그런데 여기서
if(x==0.7f) return(1);
그 프로모션은 서로 비교되는 비트 패턴과 동일하게 발생하지 않습니다.
1.0이 작동하는 이유는 무엇입니까?
00000048 <fun12>:
48: e3a005fe mov r0, #1065353216 ; 0x3f800000
4c: e12fff1e bx lr
00000050 <fun13>:
50: e3a00000 mov r0, #0
54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc>
58: e12fff1e bx lr
5c: 3ff00000 svccc 0x00f00000 ; IMB
0011111110000000...
0011111111110000000...
0 01111111 0000000...
0 01111111111 0000000...
두 경우 모두 분수는 모두 0입니다.따라서 이중에서 단일에서 이중으로 변환하는 것은 정밀도의 손실이 없습니다.이것은 정확히 싱글에서 더블로 변환되며 두 값의 비트 비교가 작동합니다.
반단이 투표하고 확인한 가장 높은 답변은 정답입니다. 이것은 혼합 정밀도의 경우이며 절대 동등한 비교를 해서는 안 됩니다.
왜 그 답에 나오지 않았는지 0.7은 1.0으로 실패합니다.0.7이 실패한 이유는 무엇입니까?중복 질문 1.1도 실패합니다.
편집
여기서 동등한 것은 문제에서 제외될 수 있습니다. 이 문제는 이미 답이 된 다른 질문이지만 동일한 문제이며 초기 충격도 있습니다.
int fun1 ( void )
{
float x=0.7;
if(x<0.7) return(1);
else return(0);
}
int fun2 ( void )
{
float x=0.6;
if(x<0.6) return(1);
else return(0);
}
Disassembly of section .text:
00000000 <fun1>:
0: e3a00001 mov r0, #1
4: e12fff1e bx lr
00000008 <fun2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
왜 하나는 다음보다 적고 다른 하나는 다음보다 작지 않습니까?그들이 동등해야 할 때.
위에서부터 우리는 0.7 이야기를 알고 있습니다.
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
01100110011001100110011000000000....
보다 작습니다.
01100110011001100110011001100110...
0.6은 0110이 아닌 다른 반복 패턴 0011입니다.
그러나 이중에서 단일 또는 일반적으로 단일 IEEE 754로 표시될 때는 변환됩니다.
00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single
IEEE 754는 올림, 올림 또는 0으로 올림 모드를 사용합니다.컴파일러는 기본적으로 반올림하는 경향이 있습니다.초등학교 12345678에서 반올림한 것을 기억한다면 위에서 세 번째 숫자로 반올림하려면 12300000이지만 뒤의 숫자가 5 이상이면 다음 숫자 1235000으로 반올림합니다.이진 1에서 5는 10의 1/2로 기본값(십진수)의 1/2이므로 반올림할 위치 뒤의 숫자가 1이면 반올림하지 않습니다.0.7은 반올림하지 않고 0.6은 반올림합니다.
그리고 이제 그것을 쉽게 볼 수 있습니다.
00110011001100110011010
(x<0.7) 때문에 이중으로 변환됩니다.
00110011001100110011010000000000....
보다 큼
00110011001100110011001100110011....
따라서 동일하게 사용하는 것에 대해 이야기할 필요 없이 0.7은 두 배, 0.7f는 단일으로 표시되며, 서로 다를 경우 가장 높은 정밀도로 작업이 진행됩니다.
다른 의견제출자들이 지적했듯이, 초기화 오류 또는 계산의 반올림 오류로 인해 == 연산자가 거짓으로 반환되는 사소한 차이가 발생할 수 있으므로 플로트 간의 정확한 동등성을 테스트하는 것은 일반적으로 안전하지 않다는 것이 문제입니다.
더 나은 관행은 다음과 같은 것을 하는 것입니다.
float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
printf("equal");
else
printf("not equal");
FLT_EPSILON이 플랫폼에 적합하게 작은 부동값으로 정의되었다고 가정합니다.
반올림 또는 초기화 오류가 FLT_EPSILON 값을 초과할 가능성은 낮으므로, 이를 통해 원하는 신뢰할 수 있는 동등성 테스트를 수행할 수 있습니다.
웹 주변의 많은 답들은 부동소수점 숫자 사이의 절대적인 차이를 보는 실수를 범합니다. 이것은 특별한 경우에만 유효합니다. 강력한 방법은 아래와 같이 상대적인 차이를 살펴보는 것입니다.
// Floating point comparison:
bool CheckFP32Equal(float referenceValue, float value)
{
const float fp32_epsilon = float(1E-7);
float abs_diff = std::abs(referenceValue - value);
// Both identical zero is a special case
if( referenceValue==0.0f && value == 0.0f)
return true;
float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) );
if(rel_diff < fp32_epsilon)
return true;
else
return false;
}
고려 사항:
int main()
{
float a = 0.7;
if(0.7 > a)
printf("Hi\n");
else
printf("Hello\n");
return 0;
}
만약 (0.7 > a) 여기서 a가 플로트 변수이고,0.7는 이중 상수입니다. 상수 중이 수상0.7 a 변 보 a 니 큽 다 다 수 보다 큽니다.되면 if 조건이 충족되면 if 조건이 됩니다.'Hi'
예:
int main()
{
float a=0.7;
printf("%.10f %.10f\n",0.7, a);
return 0;
}
력출:
0.7000000000 0.6999999881
변수에 저장된 포인팅 값과 상수의 데이터 유형이 다릅니다.데이터 유형의 정밀도 차이입니다.데이터 유형 off 변수를 double로 변경하면 동일하게 인쇄됩니다. 이는 기본적으로 double에 저장된 부동 소수점의 상수가 float보다 double의 정밀도가 높기 때문입니다.부동 소수점 숫자를 이진 변환으로 변환하는 방법을 보면 완전히 분명해질 것입니다.
언급URL : https://stackoverflow.com/questions/1839422/strange-output-in-comparison-of-float-with-float-literal
'programing' 카테고리의 다른 글
| jQuery의 replaceWith()와 html()의 차이점은 무엇입니까? (0) | 2023.08.12 |
|---|---|
| Powershell 경로에 일시적으로 추가 (0) | 2023.08.12 |
| 최대 절전 모드 매핑의 EntityNotFoundException이 있지만 데이터가 있음 (0) | 2023.08.12 |
| OpenXML SDK: Excel 재계산 공식 만들기 (0) | 2023.08.12 |
| npm 스크립트 실행 시 출력을 억제하는 방법 (0) | 2023.08.12 |

