2017-04-07 3 views
-1

두 개의 double이 거의 같은지 확인하기 위해 Java 코드를 찾으려고합니다. 나는 많은 인터넷 검색을했고 내가 여기에 모아 놓은 작은 조각들을 발견했다. 그것이 나를 벗어나기 시작하는 곳은 "상대적인 엡실론"의 사용입니다. 이 접근법은 내가 찾는 것과 같습니다. 엡실론을 직접 지정하지 않고 두 인수의 크기에 따라 엡실론을 사용하고 싶습니다. 여기에 내가 작성한 코드가 있습니다. 나는 그것에 온전한 체크가 필요합니다. (추신 : 내가 아는 충분히 수학은 위험 할 수 있습니다.)두 개의 double이 거의 같은지 확인하는 방법

public class MathUtils 
{ 
    // http://stackoverflow.com/questions/3728246/what-should-be-the- 
    // epsilon-value-when-performing-double-value-equal-comparison 
    // ULP = Unit in Last Place 
    public static double relativeEpsilon(double a, double b) 
    { 
     return Math.max(Math.ulp(a), Math.ulp(b)); 
    } 

    public static boolean nearlyEqual(double a, double b) 
    { 
     return nearlyEqual(a, b, relativeEpsilon(a, b)); 
    } 

    // http://floating-point-gui.de/errors/comparison/ 
    public static boolean nearlyEqual(double a, double b, double epsilon) 
    { 
     final double absA = Math.abs(a); 
     final double absB = Math.abs(b); 
     final double diff = Math.abs(a - b); 

     if(a == b) 
     { 
      // shortcut, handles infinities 
      return true; 
     } 
     else if(a == 0 || b == 0 || diff < Double.MIN_NORMAL) 
     { 
      // a or b is zero or both are extremely close to it 
      // relative error is less meaningful here 
      // NOT SURE HOW RELATIVE EPSILON WORKS IN THIS CASE 
      return diff < (epsilon * Double.MIN_NORMAL); 
     } 
     else 
     { 
      // use relative error 
      return diff/Math.min((absA + absB), Double.MAX_VALUE) < epsilon; 
     } 
    } 
} 
+1

하나의 ulp는 아마도 약간의 엡실론입니다. –

+0

어떻게해야합니까에 대한 권장 사항? – careysb

+0

아, @ 루이스 와서 만, 클라우스가 말한 당신 물건들 같아. – careysb

답변

1

내가 이것에 대한 라이브러리를 사용하는 것, 내가 일반적으로 사용하는 사람이 구글의 구아바 라이브러리 이리저리 DoubleMath입니다. https://google.github.io/guava/releases/19.0/api/docs/com/google/common/math/DoubleMath.html

if (DoubleMath.fuzzyEquals(a, b, epsilon)) { // a and b are equal within the tolerance given } 또한 퍼지 콤팩트가 있습니다.

+0

라이브러리의 fuzzyEqals()에는 ulp를 사용하여 피하려고하는 허용 오차 매개 변수가 필요합니다 (분명히 올바르게 수행하지 않았습니다). – careysb

+3

@careysb 라이브러리가 허용 오차를 요구하는 이유는 올바른 허용 오차가 ulp에만 의존하지 않기 때문입니다. 지금까지의 계산을 고려한 반올림 오류의 예상 범위에 따라 다릅니다. –

+0

정확하고 계산을 사용하여 실수/복식 중 하나가 작성된 경우 정밀도가 손실 될 수 있으므로 허용 오차가 필요합니다. 예를 들어'(2f/11 * 9) * 11;은 18.000002이고, '3f * 6'은 18.0과 비교할 때 비교할 때 허용 오차가 필요합니다. –

0

2 개 플로팅 a,b 값과 비교하기위한 일반적인 방법은 다음 Math.abs() 절대 값이다

if (Math.abs(a-b) <= epsilon) do_stuff_if_equal; 
else      do_stuff_if_different; 

. JAVA로 코딩하지 않으므로 double 변형을 사용해야합니다. epsilon은 차이점입니다. 언급 한 바와 같이 ulp은 너무 작습니다. 비교할 값에 적합한 값을 사용해야합니다. 그렇다면 epsilon을 계산하는 방법은 무엇입니까?

다소 까다 롭습니다. 그렇습니다. a,b의 크기를 사용할 수는 있지만, 지수가 너무 다른 경우 잘못 판정 될 수 있기 때문에 강력한 방법은 아닙니다. 대신 의미있는 값을 사용해야합니다. 예를 들어, 위치 좌표를 비교하는 경우, 엡실론은 min detail의 분율이거나 동일한 점으로 간주되는 최소 거리 여야합니다. 각도에 대해서는 1e-6 deg과 같이 작은 최소 각도이지만 그 값은 사용하는 범위와 정확도에 따라 다릅니다. 정규화 된 <-1,1> 범위의 경우 일반적으로 1e-10 또는 1e-30을 사용합니다.

엡실론은 대부분 대상 정확도와 크기에 따라 달라지며 케이스마다 다를 수 있으므로 원하는대로 epsilon을 제거 할 수있는 균일 한 방법을 만들면 안전하지 않으며 나중에 머리가 아파질뿐입니다. 에.

이 문제를 쉽게 해결하기 위해 변경할 수있는 상수 또는 변수 (계산 클래스의 경우)를 정의합니다. 대부분의 경우에 충분할 정도로 값을 기본으로 설정하고 어떤 점에서 문제가 발생하면 쉽게 변경할 수 있음을 알고 있습니다. ...

어쨌든 (위의 텍스트는 무시하고) 이를 수행 할 수 있습니다 :

if (Math.abs(a)>=Math.abs(b)) epsilon=1e-30*Math.abs(b); 
else       epsilon=1e-30*Math.abs(a); 

그러나 이것은 잘못된 결과를 초래할 수 있습니다. ulp을 계속 사용하는 경우 Max 대신 Min을 사용합니다.

관련 문제