TL; DR
- 현재 허용되는 솔루션 대신 다음 기능을 사용하여 특정 제한 상황에서 바람직하지 않은 결과를 방지하면서 잠재적으로 더 효율적입니다.
- 숫자에 대해 예상되는 부정확성을 알고 그에 따라 비교 함수에 입력하십시오.
bool nearly_equal(
float a, float b,
float epsilon = 128 * FLT_EPSILON, float relth = FLT_MIN)
{
assert(std::numeric_limits<float>::epsilon() <= epsilon);
assert(epsilon < 1.f);
if (a == b) return true;
auto diff = std::abs(a-b);
auto norm = std::min((std::abs(a) + std::abs(b)), std::numeric_limits<float>::max());
return diff < std::max(relth, epsilon * norm);
}
그래픽, 제발?
부동 소수점 수를 비교할 때 두 가지 "모드"가 있습니다.
첫 번째는이다 상대적인 차이 모드 x와는 y그 진폭이 상대적으로 간주된다 |x| + |y|. 2D로 플롯하면 다음 프로필이 제공됩니다. 여기서 녹색은 x및 y. (나는 epsilon예시를 위해 0.5를 취했다 ).

상대 모드는 "일반"또는 "충분히 큰"부동 소수점 값에 사용되는 것입니다. (나중에 더 자세히 설명합니다).
두 번째는 절대 모드로, 단순히 그 차이를 고정 된 숫자와 비교할 때입니다. 다음 프로필을 제공합니다 (다시 설명을 위해 epsilon0.5와 relth1).

이 절대 비교 모드는 "작은"부동 소수점 값에 사용됩니다.
이제 문제는 우리가이 두 가지 응답 패턴을 어떻게 연결 하는가입니다.
Michael Borgwardt의 답변에서 스위치는의 값을 기반으로하며 ( 그의 답변에서) diff아래에 있어야합니다 . 이 스위치 영역은 아래 그래프에서 빗금으로 표시됩니다.relthFloat.MIN_NORMAL

때문에 relth * epsilon것이 작은 relth녹색 패치가 차례로 솔루션을 나쁜 속성을 제공하는, 단결하지 않는다 : 우리는 숫자 같은 그 세 쌍둥이 찾을 수 있습니다 x < y_1 < y_2아직 x == y2하지만를 x != y1.

이 놀라운 예를 들어보십시오.
x = 4.9303807e-32
y1 = 4.930381e-32
y2 = 4.9309825e-32
우리는 가지고 있으며 x < y1 < y2, 실제로 y2 - x는보다 2000 배 이상 큽니다 y1 - x. 그러나 현재 솔루션을 사용하면
nearlyEqual(x, y1, 1e-4) == False
nearlyEqual(x, y2, 1e-4) == True
대조적으로, 위에서 제안 된 솔루션에서 스위치 영역은 값을 기반으로하며 |x| + |y|아래에 빗금 친 사각형으로 표시됩니다. 두 영역이 모두 정상적으로 연결되도록합니다.

또한 위의 코드에는 분기가 없으므로 더 효율적일 수 있습니다. 그와 같은 작업을 고려 max하고 abs, 사전 종종 전용 조립 지침을 가지고, 필요 분기를. 이러한 이유로이 접근 방식은 nearlyEqual스위치를에서 diff < relth로 변경하여 Michael의 문제를 해결하는 다른 솔루션보다 우수하다고 생각합니다. diff < eps * relth그러면 기본적으로 동일한 응답 패턴이 생성됩니다.
상대 비교와 절대 비교간에 전환 할 위치는 어디입니까?
이러한 모드 사이의 전환 은 허용되는 답변 relth에서와 같이 사용 FLT_MIN됩니다. 이 선택은의 표현이 float32부동 소수점 숫자의 정밀도를 제한 한다는 것을 의미합니다 .
이것은 항상 의미가있는 것은 아닙니다. 예를 들어 비교 한 숫자가 빼기의 결과 인 경우 범위 내의 어떤 FLT_EPSILON것이 더 의미가있을 수 있습니다. 뺀 숫자의 제곱근이면 수치 부정확성이 훨씬 더 높아질 수 있습니다.
부동 소수점을 0. 여기서는 상대 비교가 실패 |x - 0| / (|x| + 0) = 1합니다. 따라서 x계산이 정확하지 않은 순서 일 때 비교는 절대 모드로 전환해야 하며 거의 FLT_MIN.
이것이 relth위 의 매개 변수를 도입 한 이유입니다 .
또한, 곱하지 않음으로써 relth으로 epsilon,이 매개 변수의 해석은 우리가 그 숫자에 기대하는 수치의 정밀도 수준으로 간단하고 대응이다.
수학적 울림
(대부분 내 즐거움을 위해 여기에 보관)
더 일반적으로 나는 잘 작동하는 부동 소수점 비교 연산자 =~가 몇 가지 기본 속성을 가져야 한다고 가정합니다 .
다음은 다소 분명합니다.
- 자기 평등 :
a =~ a
- 대칭 :
a =~ b의미b =~ a
- 반대에 의한 불변성 :
a =~ b암시-a =~ -b
(우리는이없는 a =~ b및 b =~ c의미 a =~ c, =~등가 관계 없음).
부동 소수점 비교에 더 구체적인 다음 속성을 추가합니다.
- 만약
a < b < c, 다음 a =~ c의미 a =~ b(가까운 값도 동일 함)
- 만약
a, b, m >= 0다음 a =~ b의미 a + m =~ b + m(동일한 값이 큰 차이는 동일해야한다)
- 경우
0 <= λ < 1다음 a =~ b을 의미한다 λa =~ λb(아마 덜 분명 인수에 대한).
이러한 속성은 이미 가능한 거의 같음 함수에 강력한 제약을줍니다. 위에서 제안한 기능이이를 검증합니다. 하나 또는 여러 개의 명백한 속성이 누락되었을 수 있습니다.
및로 매개 변수화 된 =~평등 관계의 가족 이라고 생각할 때 다음 을 추가 할 수도 있습니다.=~[Ɛ,t]Ɛrelth
- 경우
Ɛ1 < Ɛ2다음 a =~[Ɛ1,t] b을 의미한다 a =~[Ɛ2,t] b(주어진 허용 오차에 대한 평등은 더 높은 허용 오차에서 평등을 의미한다)
- 경우
t1 < t2다음 a =~[Ɛ,t1] b을 의미한다 a =~[Ɛ,t2] b(주어진 부정확 평등은 더 높은 부정확성에서 평등을 의미한다)
제안 된 솔루션은 또한이를 확인합니다.