programing

부동 소수점 값을 비교하는 것은 얼마나 위험한가요?

kingscode 2022. 8. 18. 21:27
반응형

부동 소수점 값을 비교하는 것은 얼마나 위험한가요?

는 알고 있다UIKitCGFloat분해능 독립 좌표계 때문입니다.

하지만 매번 확인하고 싶을 때마다 예를 들어frame.origin.x0속이 안 좋아지는군.

if (theView.frame.origin.x == 0) {
    // do important operation
}

그래CGFloatfalse positive와 할 때하다.==,<=,>=,<,>? 하지 않은 문제가 0.0000000000041예를들면.

나나?Objective-C할 때, 비교할 때 하거나, 또는 그 경우에 할 수 .origin.x으로 표시되는 은 0과 .0★★★★★★★★★★★★★★★★★★?

우선 부동소수점 값은 동작상 '랜덤'이 아닙니다.정확한 비교는 많은 실제 사용에서 의미가 있습니다.하지만 부동 소수점을 사용하려면 어떻게 작동하는지 알아야 합니다.부동소수가 실수처럼 기능한다고 가정하면 코드를 빠르게 해독할 수 있습니다.부동소수점 결과에 큰 랜덤 퍼즈가 관련지어져 있다고 가정하면(여기서 나타내는 대부분의 답변과 마찬가지로), 처음에는 동작하는 것처럼 보이지만, 실제로는 큰 규모의 에러나 파손된 코너 케이스가 발생하는 코드를 얻을 수 있습니다.

우선 부동 소수점을 사용하여 프로그래밍하려면 다음을 읽어야 합니다.

부동 소수점 산술에 대해 컴퓨터 과학자가 알아야 할 사항

네, 다 읽어보세요.너무 부담스럽다면 읽을 시간이 생길 때까지 정수/고정점을 계산에 사용해야 합니다. :-)

이와 같이 부동 소수점 비교의 가장 큰 문제는 다음과 같습니다.

  1. .scanf ★★★★★★★★★★★★★★★★★」strtod부동소수점 값으로 존재하지 않고 가장 가까운 근사치로 자동 변환됩니다.이것이 바로 demon9733의 대답입니다.

  2. 실제 결과를 나타낼 정도의 정밀도가 부족하기 때문에 많은 결과가 반올림됩니다.을 알 수 있는 '더하기'입니다.x = 0x1fffffe ★★★★★★★★★★★★★★★★★」y = 1여기 기기, 기기, 여,,x는 가수(와 가수(의 정밀도를 있습니다.y에는 1비트밖에 없지만 추가 시 비트가 겹치는 위치에 있지 않기 때문에 25비트의정밀도가 높아집니다.대신,(반올림됩니다 대신으로 반올림을 가져옵니다.0x2000000기본 사용하여 반올림 모드에서.디폴트반올림 모드 cm이다.

  3. 정확한 값을 얻기 위해 무한히 많은 장소가 필요하기 때문에 많은 결과가 반올림됩니다.여기에는 1/3과 같은 합리적인 결과(소수점부터 무한히 많은 자리)와 1/10(5는 2의 거듭제곱이 아니기 때문에 무한히 많은 자리)과 완벽한 제곱이 아닌 모든 것의 제곱근과 같은 비합리적인 결과도 포함됩니다.

  4. 더블 라운딩.일부 시스템(특히 x86)에서는 부동소수점 식은 공칭 유형보다 높은 정밀도로 평가됩니다.즉, 위의 반올림 유형 중 하나가 발생하면 먼저 결과를 정밀도가 높은 유형으로 반올림한 후 최종 유형으로 반올림하는 두 가지 반올림 단계가 수행됩니다.예를 들어 1.49를 정수(1)로 반올림한 경우의 결과와 소수점(1.5)으로 반올림한 경우의 결과를 정수(2)로 반올림한 경우의 결과를 비교합니다.컴파일러의 동작(특히 버그가 있는 GCC와 같은 부적합 컴파일러의 경우)은 예측할 수 없기 때문에 이것은 부동 소수점 내에서 가장 다루기 어려운 영역 중 하나입니다.

  5. 초월 기능(초월함수(trig,,exp,,log, 등)은 올바르게 반올림된 결과를 지정하지 않습니다.결과는 정밀도의 마지막 자리(통상 1ulp)에 있는 하나의 단위 내에서만 정확하도록 지정됩니다.

부동소수점 코드를 작성할 때는 결과가 부정확할 수 있는 수치로 무엇을 하고 있는지 염두에 두고 그에 따라 비교해야 합니다.종종 엡실론과 비교하는 것이 타당하지만, 엡실론은 절대 상수가 아니라 비교하고 있는 숫자의 크기를 기반으로 해야 합니다.(절대 상수 엡실론이 작동하는 경우, 이는 부동점이 아닌 고정점이 작업에 적합한 도구임을 강력히 나타냅니다.)

편집: 특히 매그니튜드에 상대적인 엡실론 검사는 다음과 같습니다.

if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))

어디 어디에FLT_EPSILON그로부터의 상수입니다에서 끊임없이 있다.float.h((로 대체로 교체하는 것)DBL_EPSILONfor위해서doubles또는 또는LDBL_EPSILON위해서에long doubles)과 s)및K당신은 당신의 계산의 누적 오류 확실히 계산의에 의해 누적 오차가 확실하게 다음과 같이 제한되도록 선택하는 상수입니다를 다스릴 수 있는 그러한 선택한 상수입니다.K마지막으로 단위(확실하지 않고 오류를 계산할 권리 만드(오류한계 계산을 제대로 했는지 확실하지 않은 경우를 받았어).K을 사용하다

이것을 사용할 0에 할 수 있습니다. 0은 0에 가깝기 때문입니다.FLT_EPSILON같이 입니다.간단한 해결 방법은 다음과 같습니다.

if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y) || fabs(x-y) < FLT_MIN)

그리고 마찬가지로 대체한다.DBL_MINdouble을 .

번호로서 하게 나타낼 수 지금까지의 다른 ), 는 아마입니다.수 예: " " " " " " " " " 등).theView.frame.origin.x0.0을 보장할 수 0이 되어야 합니다.

간단히 설명하자면 다음과 같은 계산입니다.

areal = 0.0

will(언어 또는 시스템이 깨지지 않는 한)은 (면적==0.0) true를 반환하지만 다음과 같은 또 다른 계산이 반환되는 값을 생성합니다.

areal = 1.386 - 2.1*(0.66)

아닐지도 모른다.

계산에서 0이 되어야 하는 값을 생성하는 것뿐만 아니라 0이 되어야 하는 값도 계산에서 생성된다고 확신할 수 있는 경우에는 f-p 값을 0과 비교할 수 있습니다.만약 당신이 요구되는 정도까지 자신을 확신시킬 수 없다면, '관용된 평등'이라는 일반적인 접근법을 고수하는 것이 최선이다.

최악의 경우 f-p 값을 부주의하게 비교하는 것은 매우 위험할 수 있습니다. 즉, 항공전자학, 무기 지침, 발전소 운영, 차량 내비게이션, 연산이 실제 세계와 일치하는 거의 모든 애플리케이션입니다.

앵그리버드에게는 그리 위험하지 않다.

나는 다른 사람들과 조금 다른 대답을 하고 싶다.이러한 질문에는 기재된 바와 같이 답변하는 것이 좋지만, 알아야 할 사항이나 실제 문제가 무엇인지에 대해서는 답변하지 못할 수 있습니다.

그래픽스 부동소수점 문제 없습니다!하지만 수레를 직접 비교할 필요는 거의 없다.왜 그래야 하는데?그래픽스는 플로트를 사용하여 간격을 정의합니다.그리고 플로트가 플로트에 의해 정의된 간격 내에 있는지 비교하는 것은 항상 잘 정의되어 있으며 정확하거나 정확하지 않고 일관성이 있어야 합니다.픽셀(인터벌이기도 합니다!)을 할당할 수 있는 한, 그래픽스 요건은 이것뿐입니다.

따라서 점이 [0]의 범위를 벗어나는지 여부를 테스트하고자 하는 경우.width [ range ]이 정도면 충분합니다.일관되게 포함을 정의해야 합니다.예를 들어 inside는 항상 (x>=0 & x < width )라고 정의합니다.교차로나 히트 테스트도 마찬가지다.

그러나 그래픽 좌표를 일종의 플래그(예: 창이 도킹되었는지 확인)로 악용하는 경우에는 이 작업을 수행하지 마십시오.대신 그래픽 표시 레이어와 별도의 부울 플래그를 사용하십시오.

0과 비교하는 것은 0이 계산된 값이 아닌 한 안전할 수 있습니다(위 답변에서 언급).그 이유는 부동 소수점에서는 0이 완벽하게 표현 가능한 숫자이기 때문입니다.

완벽하게 표현 가능한 값을 사용하면 24비트의 범위를 2의 제곱(단일 정밀도) 개념으로 얻을 수 있습니다.따라서 1, 2, 4는 .5, .25 및 .125와 같이 완벽하게 표현됩니다.중요한 비트가 모두 24비트인 한, 당신은 황금입니다.따라서 10.625는 정확히 뉘우칠 수 있습니다.

이것은 훌륭하지만, 압박감에 의해 곧 무너질 것입니다.두 가지 시나리오가 갑자기 떠오릅니다. 1) 계산이 관련된 경우.sqrt(3)*sqrt(3) == 3을 신뢰하지 마십시오.그런 식으로는 안 될 거야.그리고 다른 답변에서 알 수 있듯이 엡실론 내에는 없을 것입니다.2) 비파워 오브 2(NPOT)가 관여하고 있는 경우.따라서 이상하게 들릴 수 있지만 0.1은 이진수로 무한 급수이므로 이와 같은 숫자와 관련된 계산은 처음부터 정확하지 않습니다.

(오, 그리고 원래 질문에서 0에 대한 비교를 언급했습니다.-0.0도 완전히 유효한 부동소수점 값임을 잊지 마십시오.)

은 [정답을 고르는 을 얼버무린다]K· 「 」를 K하면 ad-internal이 .VISIBLE_SHIFT, but, 선택하다K 눈에 띄다VISIBLE_SHIFT디스플레이 속성을 기반으로 하지 않습니다.독을 을 선택합니다.K [ ]를 선택합니다.VISIBLE_SHIFT은 '선정하다'를 VISIBLE_SHIFT에 고르다, 고르다, 고르다, 고르다, 고르다, 고르다, 고르다, 고르다, , 고르다, 고르다, 고르다, 고르다, 고르다, , 고르다.K

정확히 라운드 오류로 인해 논리적 연산에 '정확한' 값 비교를 사용하면 안 됩니다.시각 디스플레이 상의 특정 위치의 경우 위치가 0.0인지 0.0000000003인지는 중요하지 않습니다. 차이는 눈으로 볼 수 없습니다.따라서 다음과 같은 논리가 필요합니다.

#define VISIBLE_SHIFT    0.0001        // for example
if (fabs(theView.frame.origin.x) < VISIBLE_SHIFT) { /* ... */ }

그러나 결국 '눈에 보이지 않음'은 디스플레이 속성에 따라 달라집니다.으로 할 수 수 것)는, 「」( 「」)를 선택합니다.VISIBLE_SHIFT그 상한의 극히 일부입니다.

'은 ㄴ, ㄴ, ㄴ, ㄴ, ㄴ에 .K고르는 법을 요?K'먹다'는 것을 말합니다.

K는 계산의 누적 오차가 마지막 위치에 있는 K 단위로 확실하게 제한되도록 선택하는 상수입니다(오류 한계 계산이 제대로 되었는지 확실하지 않으면 계산에서 말하는 것보다 몇 배 더 크게 K를 만드십시오).

가 필요한 것은K...을 취득하는 경우K보다 더덜입니다.VISIBLE_SHIFT당신에게 어떤 것이 좋을지 결정하게 될 겁니다.K에는 시험 .K이치노.K을 쓸 '정답'은요 '정답'은요?

'정답' 세부사항으로 사용합니다.

if (fabs(x-y) < K * DBL_EPSILON * fabs(x+y) || fabs(x-y) < DBL_MIN)

K의 모든 값을 사용해 보겠습니다.

#include <math.h>
#include <float.h>
#include <stdio.h>

void main (void)
{
  double x = 1e-13;
  double y = 0.0;

  double K = 1e22;
  int i = 0;

  for (; i < 32; i++, K = K/10.0)
    {
      printf ("K:%40.16lf -> ", K);

      if (fabs(x-y) < K * DBL_EPSILON * fabs(x+y) || fabs(x-y) < DBL_MIN)
        printf ("YES\n");
      else
        printf ("NO\n");
    }
}
ebg@ebg$ gcc -o test test.c
ebg@ebg$ ./test
K:10000000000000000000000.0000000000000000 -> YES
K: 1000000000000000000000.0000000000000000 -> YES
K:  100000000000000000000.0000000000000000 -> YES
K:   10000000000000000000.0000000000000000 -> YES
K:    1000000000000000000.0000000000000000 -> YES
K:     100000000000000000.0000000000000000 -> YES
K:      10000000000000000.0000000000000000 -> YES
K:       1000000000000000.0000000000000000 -> NO
K:        100000000000000.0000000000000000 -> NO
K:         10000000000000.0000000000000000 -> NO
K:          1000000000000.0000000000000000 -> NO
K:           100000000000.0000000000000000 -> NO
K:            10000000000.0000000000000000 -> NO
K:             1000000000.0000000000000000 -> NO
K:              100000000.0000000000000000 -> NO
K:               10000000.0000000000000000 -> NO
K:                1000000.0000000000000000 -> NO
K:                 100000.0000000000000000 -> NO
K:                  10000.0000000000000000 -> NO
K:                   1000.0000000000000000 -> NO
K:                    100.0000000000000000 -> NO
K:                     10.0000000000000000 -> NO
K:                      1.0000000000000000 -> NO
K:                      0.1000000000000000 -> NO
K:                      0.0100000000000000 -> NO
K:                      0.0010000000000000 -> NO
K:                      0.0001000000000000 -> NO
K:                      0.0000100000000000 -> NO
K:                      0.0000010000000000 -> NO
K:                      0.0000001000000000 -> NO
K:                      0.0000000100000000 -> NO
K:                      0.0000000010000000 -> NO

아, 1e-13을 '제로'로 하려면 K는 1e16 이상이어야 합니다.

두 가지 선택지가 있습니다

  1. 제가 제안한 바와 같이 '엡실론'의 값에 대한 공학적 판단을 사용하여 간단한 엡실론 계산을 수행합니다.그래픽스 작업을 할 때 '0'이 '눈에 보이는 변화'를 의미하는 경우 시각적 자산(이미지 등)을 살펴보고 엡실론이 무엇인지 판단하십시오.
  2. 카고 컬트가 아닌 답변의 참조를 읽고 박사학위를 취득할 때까지 부동소수점 계산을 시도하지 마십시오.D)를 선택한 D)를 선택합니다.K.

정답: 코코아 터치 포인트 비교 방법

정답은 CGPointEqualToPoint()입니다.

다른 질문:계산된 두 값이 같습니까?

답변은 다음과 같습니다.그들은 그렇지 않다.

가까운지 어떻게 확인하나요?근접 여부를 확인하려면 CGPointEqualToPoint()를 사용하지 마십시오.하지만 가까운지 확인은 하지 마세요.점이 선을 넘어섰는지 또는 점이 구 안에 있는지 확인하는 등 실제 세계에서 의미가 있는 작업을 수행합니다.

-(BOOL)isFloatEqual:(CGFloat)firstValue secondValue:(CGFloat)secondValue{

BOOL isEqual = NO;

NSNumber *firstValueNumber = [NSNumber numberWithDouble:firstValue];
NSNumber *secondValueNumber = [NSNumber numberWithDouble:secondValue];

isEqual = [firstValueNumber isEqualToNumber:secondValueNumber];

return isEqual;

}

소수점 이하를 비교하기 위해서 다음의 비교 함수를 사용하고 있습니다.

bool compare(const double value1, const double value2, const int precision)
{
    int64_t magnitude = static_cast<int64_t>(std::pow(10, precision));
    int64_t intValue1 = static_cast<int64_t>(value1 * magnitude);
    int64_t intValue2 = static_cast<int64_t>(value2 * magnitude);
    return intValue1 == intValue2;
}

// Compare 9 decimal places:
if (compare(theView.frame.origin.x, 0, 9)) {
    // do important operation
}

플로트를 0과 비교하기 위해 다음과 같은 코드를 사용할 수 있습니다.

if ((int)(theView.frame.origin.x * 100) == 0) {
    // do important operation
}

이는 0.1의 정확도와 비교되며, 이 경우 CGFloat에 충분한 정확도입니다.

마지막으로 C 표준을 확인했을 때 더블(총 64비트, 53비트 가수)에 대한 부동소수점 연산이 이 정도 이상으로 정확해야 할 필요는 없었습니다.단, 일부 하드웨어는 보다 정밀하게 레지스터에서 작업을 수행할 수 있으며, 이 요건은 하위 비트를 클리어할 필요가 없음을 의미하는 것으로 해석되었습니다(레지스터에 로드되는 숫자의 정밀도를 넘어).따라서 레지스터에 남아 있는 것이 누구로부터 마지막으로 잤는지에 따라 이와 같은 예상 밖의 비교 결과를 얻을 수 있습니다.

그렇긴 하지만, 제가 볼 때마다 지워버리려고 노력했지만, 제가 일하는 의상에는 gcc를 사용하여 컴파일되어 Linux에서 실행되는 C코드가 많이 있고, 오랜 시간 동안 전혀 예상치 못한 결과를 눈치채지 못했습니다.이것이 gcc가 하위 비트를 클리어하기 때문인지, 80비트 레지스터가 최신 컴퓨터에서는 이러한 작업에 사용되지 않았는지, 표준이 변경되었는지 등에 대해서는 알 수 없습니다.요점을 인용할 수 있는 사람이 있는지 알고 싶습니다.

각 숫자를 개체로 선언하고 그 개체에서 세 가지를 정의하는 것이 옳다고 생각합니다. 1) 등식 연산자.2) set Acceptable Difference 메서드.3) 가치 그 자체.등호 연산자는 두 값의 절대 차이가 허용 가능한 값으로 설정된 값보다 작을 경우 true를 반환합니다.

문제에 맞게 개체를 하위 분류할 수 있습니다.예를 들어, 1인치와 2인치 사이의 둥근 금속 막대는 지름 차이가 0.0001인치 미만일 경우 동일한 직경으로 간주할 수 있습니다.따라서 매개 변수 0.0001을 사용하여 setAcceptableDifference를 호출한 다음 자신 있게 등호 연산자를 사용할 수 있습니다.

언급URL : https://stackoverflow.com/questions/10334688/how-dangerous-is-it-to-compare-floating-point-values

반응형