programing

float 리터럴을 사용한 float 비교의 이상한 출력

sourcejob 2023. 8. 12. 10:14
반응형

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다합.

enter image description here
는 "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)로 곱합니다.다음과 같은 정보를 얻을 수 있습니다.


enter image description here enter image description here

우리는 모든 숫자(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을 초과하면 계속합니다.
enter image description here
그리고 번호는 .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

반응형