부동 소수점 값이 0과 같은지 확인하는 것이 안전합니까?


100

일반적으로 double 또는 decimal 유형 값 사이의 평등에 의존 할 수 없다는 것을 알고 있지만 0이 특별한 경우인지 궁금합니다.

0.00000000000001과 0.00000000000002 사이의 부정확성을 이해할 수 있지만 0 자체는 아무것도 아니기 때문에 엉망이되기가 꽤 어렵습니다. 아무것도 정확하지 않다면 더 이상 아무것도 아닙니다.

하지만 저는이 주제에 대해 잘 모르기 때문에 제가 말할 것도 없습니다.

double x = 0.0;
return (x == 0.0) ? true : false;

그게 항상 사실로 돌아 올까요?


69
삼항 연산자는 해당 코드에서 중복됩니다. :)
Joel Coehoorn

5
LOL 당신이 맞아요. Go me
Gene Roberts

x가 어떻게 0으로 설정되었는지 모르기 때문에 나는 그것을하지 않을 것입니다. 그래도 계속하고 싶다면 끝 부분에 태그가 붙은 1e-12 등을 제거하기 위해 둥글거나 바닥 x를 원할 것입니다.
Rex Logan

답변:


115

double 변수에 정확히 값이있는 경우에만 비교 결과가 반환 될 것으로 예상하는 것이 안전 합니다 (원래 코드 스 니펫에서는 물론 해당됨). 이것은 연산자 의 의미와 일치합니다 . " 는 " 와 같다는 의미 입니다.true0.0==a == bab

그것은이다 안전하지 (이 때문에 정확하지 ) 일부 계산의 결과가 순수 수학에서 같은 계산의 결과가 0 때마다 두 배 (또는 더 일반적으로, 부동 소수점)를 arithmetics 제로가 될 것으로 기대합니다. 그 이유는 계산이 시작되면 부동 소수점 정밀도 오류가 나타나기 때문입니다. 이는 수학의 실수 산술에는 존재하지 않는 개념입니다.


51

"동등"비교를 많이해야하는 경우 비교를 위해 .NET 3.5에서 약간의 도우미 함수 또는 확장 메서드를 작성하는 것이 좋습니다.

public static bool AlmostEquals(this double double1, double double2, double precision)
{
    return (Math.Abs(double1 - double2) <= precision);
}

다음과 같은 방법으로 사용할 수 있습니다.

double d1 = 10.0 * .1;
bool equals = d1.AlmostEquals(0.0, 0.0000001);

4
이 숫자가 서로 매우 가까운 값을 갖는 경우 double1과 double2를 비교하면 감산 취소 오류가 발생할 수 있습니다. 나는 Math.Abs를 제거하고 각 지점을 확인 할 개별적으로 D1> = D2 - 전자 및 D1 <= D2 + 전자
시어 도어 Zographos

"Epilon은 범위가 0에 가까운 양수 값의 최소 표현을 정의하기 때문에 유사한 두 값 사이의 차이 한계가 Epsilon보다 커야합니다. 일반적으로 Epsilon보다 몇 배 더 큽니다.이 때문에 다음 작업을 수행하는 것이 좋습니다. Double 값이 같은지 비교할 때 Epsilon을 사용하지 마십시오. " - msdn.microsoft.com/en-gb/library/ya2zha7s(v=vs.110).aspx
라파엘 코스타

15

간단한 샘플의 경우 해당 테스트는 괜찮습니다. 하지만 이것에 대해 :

bool b = ( 10.0 * .1 - 1.0 == 0.0 );

.1은 이진수로 반복되는 10 진수이며 정확하게 표현할 수 없습니다. 그런 다음 다음 코드와 비교하십시오.

double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away
bool b = ( d1 - 1.0 == 0.0 );

실제 결과를 확인하기 위해 테스트를 실행하도록 남겨 둘 것입니다. 그렇게 기억할 가능성이 더 큽니다.


5
실제로 이것은 어떤 이유로 (LINQPad에서) true를 반환합니다.
Alexey Romanov

당신이 말하는 ".1 이슈"는 무엇입니까?
Teejay

14

Double.Equals에 대한 MSDN 항목에서 :

비교 정밀도

Equals 메서드는 두 값의 정밀도가 다르기 때문에 분명히 동일한 두 값이 같지 않을 수 있으므로주의해서 사용해야합니다. 다음 예제에서는 Double 값 .3333과 1을 3으로 나누어 반환 된 Double이 같지 않다고보고합니다.

...

동등성을 비교하는 대신 권장되는 한 가지 기술은 두 값 간의 허용 가능한 차이 한계를 정의하는 것입니다 (예 : 값 중 하나의 .01 %). 두 값 간의 차이의 절대 값이 해당 여백보다 작거나 같으면 차이는 정밀도 차이로 인한 것일 수 있으므로 값이 같을 가능성이 있습니다. 다음 예제에서는이 기술을 사용하여 이전 코드 예제에서 같지 않은 것으로 확인 된 두 Double 값인 .33333과 1/3을 비교합니다.

또한 Double.Epsilon을 참조하십시오 .


1
동등하지 않은 값이 같은 것으로 비교할 수도 있습니다. 경우 하나는을 기대 x.Equals(y)하고 (1/x).Equals(1/y)있지만,이 경우에는 그렇지 않습니다 xIS0 하고 y있다 1/Double.NegativeInfinity. 이러한 값은 상호가 같지 않더라도 동일하다고 선언합니다.
supercat 2012-09-26

@supercat : 동등합니다. 그리고 그들은 상호가 없습니다. x = 0및로 테스트를 다시 실행할 수 있습니다.y = 0 , 당신은 여전히 찾을 것입니다 1/x != 1/y.
Ben Voigt

@BenVoigt : xy as type double? 결과를 어떻게 비교하여 불평등하게보고합니까? 1 / 0.0은 NaN이 아닙니다.
supercat

@supercat : 좋아, IEEE-754가 잘못한 것 중 하나입니다. (먼저1.0/0.0 그것이 있어야로 제한이 고유하지로, NaN이 될하지 둘째, 무한대는 서로 동일 비교하는 것이 무한대의도에 어떤 관심을 지불하지 않고.)
벤 보이트

@BenVoigt : 0이 두 개의 아주 작은 숫자를 곱한 결과라면 1.0을 그 숫자로 나누면 어떤 숫자보다 큰 숫자가 같은 부호를 가지고 있고 작은 숫자 중 하나가 작은 숫자보다 작은 값을 비교하는 값이 생성됩니다. 숫자에는 반대 기호가 있습니다. IMHO, IEEE-754는 부호없는 0이 있지만 양수 및 음수 무한소가 있으면 더 좋습니다.
supercat

6

문제는 부동 소수점 값 구현의 다른 유형을 비교할 때 발생합니다 (예 : float와 double 비교). 하지만 같은 타입이라면 문제가되지 않습니다.

float f = 0.1F;
bool b1 = (f == 0.1); //returns false
bool b2 = (f == 0.1F); //returns true

문제는 프로그래머가 때때로 암시 적 유형 캐스트 ​​(double to float)가 비교를 위해 발생하고 그 결과 버그가 발생한다는 사실을 잊어 버리는 것입니다.


3

숫자가 float 또는 double에 직접 할당 된 경우 0 또는 double의 경우 53 비트로, float의 경우 24 비트로 나타낼 수있는 정수에 대해 테스트하는 것이 안전합니다.

또는 다른 방법으로 말하면 항상 double에 정수 값을 할당 한 다음 double을 동일한 정수와 다시 비교하여 동일하다는 것을 보장 할 수 있습니다.

정수를 할당하여 시작하여 정수를 더하거나 빼거나 곱하는 방식을 고수하여 간단한 비교를 계속할 수 있습니다 (결과가 float abd의 경우 24 비트 미만이라고 가정하면 double의 경우 53 비트). 따라서 특정 제어 조건에서 float 및 double을 정수로 처리 할 수 ​​있습니다.


나는 일반적으로 귀하의 진술에 동의하고 찬성했지만 IEEE 754 부동 소수점 구현이 사용되는지 여부에 실제로 달려 있다고 생각합니다. 그리고 나는 모든 "현대"컴퓨터가 적어도 플로트의 저장을 위해 IEEE 754를 사용한다고 믿습니다 (다양한 기이 한 반올림 규칙이 있습니다).
Mark Lakata 2017 년

2

아니요, 괜찮지 않습니다. 소위 비정규 화 된 값 (비정규 화)은 0.0과 비교 될 때 거짓 (0이 아님)으로 비교되지만 방정식에 사용되면 정규화 (0.0이 됨)가됩니다. 따라서 이것을 0으로 나누는 것을 방지하는 메커니즘으로 사용하는 것은 안전하지 않습니다. 대신 1.0을 더하고 1.0과 비교하십시오. 이렇게하면 모든 비법 선이 0으로 처리됩니다.


비정규
Manuel

비법 선은 사용시 0이되지 않지만 정확한 작업에 따라 동일한 결과를 생성 할 수도 있고 생성하지 않을 수도 있습니다.
wnoise 2011 년

-2

이것을 시도하면 ==가 double / float에 대해 신뢰할 수 없다는 것을 알 수 있습니다.
double d = 0.1 + 0.2; bool b = d == 0.3;

Quora 의 답변 입니다.


-4

실제로 이중 값을 0.0과 비교하려면 다음 코드를 사용하는 것이 좋습니다.

double x = 0.0;
return (Math.Abs(x) < double.Epsilon) ? true : false;

플로트와 동일 :

float x = 0.0f;
return (Math.Abs(x) < float.Epsilon) ? true : false;

5
아니요. double.Epsilon의 문서에서 : "두 부동 소수점 숫자가 같은 것으로 간주 될 수 있는지 여부를 결정하는 사용자 지정 알고리즘을 만드는 경우 허용 가능한 절대 차이 한계를 설정하려면 Epsilon 상수보다 큰 값을 사용해야합니다. 두 값이 동일한 것으로 간주 될 경우 (전형적으로, 그 차이의 마진 엡실론보다 몇 배 더 크다.) ".
알라 동물의 위

1
@AlastairMaw 이것은 모든 크기의 두 배가 같은지 확인하는 데 적용됩니다. 0과 같은지 확인하려면 double.Epsilon이 좋습니다.
jwg 2013 년

4
아니, 아니야 . 일부 계산을 통해 도달 한 값이 0에서 몇 배 엡실론 떨어져있을 가능성이 높지만 여전히 0으로 간주해야합니다. 0에 가까워지기 때문에 어딘가에서 중간 결과에서 마법처럼 추가 정밀도를 얻을 수 없습니다.
Alastair Maw 2013 년

4
예 : (1.0 / 5.0 + 1.0 / 5.0-1.0 / 10.0-1.0 / 10.0-1.0 / 10.0-1.0 / 10.0) <double. Epsilon == false (그리고 크기 측면에서 상당히 그렇습니다 : 2.78E-17 vs 4.94E -324)
Alastair Maw 2013 년

그렇다면 double.Epsilon이 좋지 않은 경우 권장되는 정밀도는 무엇입니까? 엡실론의 10 배는 괜찮을까요? 100 번?
liang
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.