이 java.sun 페이지 에 따르면 ==
Java의 부동 소수점 숫자에 대한 동등 비교 연산자가 있습니다.
그러나이 코드를 입력하면 :
if(sectionID == currentSectionID)
내 편집기로 정적 분석을 실행하면 "JAVA0078 부동 소수점 값 =="
==
부동 소수점 값을 비교 하는 데 어떤 문제가 있습니까? 올바른 방법은 무엇입니까?
이 java.sun 페이지 에 따르면 ==
Java의 부동 소수점 숫자에 대한 동등 비교 연산자가 있습니다.
그러나이 코드를 입력하면 :
if(sectionID == currentSectionID)
내 편집기로 정적 분석을 실행하면 "JAVA0078 부동 소수점 값 =="
==
부동 소수점 값을 비교 하는 데 어떤 문제가 있습니까? 올바른 방법은 무엇입니까?
답변:
'평등'에 대한 수레를 테스트하는 올바른 방법은 다음과 같습니다.
if(Math.abs(sectionID - currentSectionID) < epsilon)
여기서 엡실론은 원하는 정밀도에 따라 0.00000001과 같은 매우 작은 수입니다.
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
그 문제를 해결하기 위해 기능을 변경할 수 있습니까?
Math.ulp()
이 질문에 대한 답을 제안 했다.
부동 소수점 값은 약간 씩 벗어날 수 있으므로 정확하게 같은 것으로보고되지 않을 수 있습니다. 예를 들어, float를 "6.1"로 설정 한 다음 다시 인쇄하면 "6.099999904632568359375"와 같은 값이보고 될 수 있습니다. 이것은 수레가 작동하는 방식의 기본입니다. 따라서 등식을 사용하여 비교하지 않고 범위 내에서 비교합니다. 즉, 부동 소수점의 숫자가 비교하려는 숫자와의 차이가 특정 절대 값보다 작은 경우.
Register에 관한 이 기사는 이것이 왜 그런지에 대한 좋은 개요를 제공합니다. 유용하고 흥미로운 독서.
다른 사람들이 말하는 내용의 이유를 설명하기 위해서입니다.
플로트의 이진 표현은 일종의 성가신입니다.
이진에서 대부분의 프로그래머는 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d의 상관 관계를 알고 있습니다.
다른 방법으로도 작동합니다.
.1b = .5d, .01b = .25d, .001b = .125, ...
문제는 .1, .2, .3 등과 같이 대부분의 십진수를 나타내는 정확한 방법이 없다는 것입니다. 당신이 할 수있는 모든 것은 이진법으로 대략적인 것입니다. 숫자가 인쇄 될 때 시스템은 약간의 퍼지 반올림을 수행하여 .10000000000001 또는 .999999999999 대신 0.1을 표시합니다.
의견 편집 : 이것이 문제인 이유는 우리의 기대입니다. 우리는 .7 또는 .67 또는 .666667과 같이 소수점으로 변환 할 때 2/3가 퍼지 될 것으로 예상합니다. 그러나 .1이 2/3과 같은 방식으로 반올림되는 것을 자동으로 기대하지는 않습니다. 그리고 그것은 정확히 일어나고있는 일입니다.
그건 그렇고, 당신이 내부에 저장하는 숫자가 궁금하다면 이진 "과학 표기법"을 사용하는 순수한 이진 표현입니다. 따라서 10.75d를 저장하도록 지시하면 10은 1010b, 10은 .11b를 저장합니다. 따라서 101011을 저장하고 마지막에 몇 비트를 저장합니다. 소수점을 네 자리 오른쪽으로 이동합니다.
(기술적으로는 더 이상 소수점이 아니지만 이제는 이진 점이지만 해당 용어는 어떤 용도로든이 답변을 찾는 대부분의 사람들에게 상황을 더 잘 이해할 수있게하지 못했습니다.)
부동 소수점 값을 비교하기 위해 ==를 사용하면 무엇이 잘못됩니까?
사실이 아니기 때문에 0.1 + 0.2 == 0.3
Float.compare(0.1f+0.2f, 0.3f) == 0
?
플로트 (및 복식) 주위에 많은 혼란이 있다고 생각합니다. 정리하는 것이 좋습니다.
표준 호환 JVM [*] 에서 float를 ID로 사용하는 데 본질적으로 잘못된 것은 없습니다 . float ID를 단순히 x로 설정하고 아무 것도하지 않고 (즉, 산술을하지 않음) 나중에 y == x를 테스트하면 괜찮을 것입니다. 또한 HashMap에서 키로 사용하는 데 아무런 문제가 없습니다. 당신이 할 수없는 것은 x == (x - y) + y
등의 등식을 가정하는 것입니다 . 사람들은 일반적으로 정수 유형을 ID로 사용하며 여기에있는 대부분의 사람들 이이 코드에 의해 벗어난 것을 볼 수 있으므로 실제적인 이유로 규칙을 따르는 것이 좋습니다 . double
길이 만큼 많은 값이 values
있으므로를 사용하면 아무 것도 얻지 못합니다 double
. 또한 "사용 가능한 다음 ID"생성은 두 배로 까다로울 수 있으며 부동 소수점 산술에 대한 지식이 필요합니다. 문제의 가치가 없습니다.
반면에 두 개의 수학적으로 동등한 계산 결과의 수치 적 동등성에 의존하는 것은 위험합니다. 이는 10 진수에서 이진 표현으로 변환 할 때 반올림 오류 및 정밀도 손실로 인한 것입니다. 이것은 SO에 대해 논의되었습니다.
[*] "표준 호환 JVM"이라고 말하면 특정 뇌 손상 JVM 구현을 제외하고 싶었습니다. 참조 이 .
==
하지 않고 비교 equals
하거나 다른 것과 비교하는 float이 테이블에 저장되지 않도록주의해야 합니다. 그렇지 않으면, 다양한 입력이 공급 될 때 식에서 생성 될 수있는 고유 한 결과 수를 세는 프로그램은 모든 NaN 값을 고유 한 것으로 간주 할 수 있습니다.
Float
을 의미 float
합니다.
Float
? 고유 한 float
값 의 테이블을 작성하려고하고 이를 값과 비교 ==
하면 끔찍한 IEEE-754 비교 규칙으로 인해 테이블에 NaN
값 이 넘칠 수 있습니다.
float
유형에는 equals
메소드 가 없습니다 .
equals
인스턴스 메소드를 의미하는 것이 아니라 Float
타입의 두 값을 비교 하는 정적 메소드 ( 클래스 내에서 생각 )입니다 float
.
이것은 Java에만 국한되지 않는 문제입니다. ==를 사용하여 두 개의 float / doubles / 십진수 유형 번호를 비교하면 저장 방식으로 인해 잠재적으로 문제가 발생할 수 있습니다. 단 정밀도 부동 소수점 (IEEE 표준 754에 따라)에는 32 비트가 있으며 다음과 같이 배포됩니다.
1 비트-부호 (0 = 양수, 1 = 음수)
8 비트-지수 (2 ^ x에서 x의 특수 (bias-127) 표현)
23 비트-Mantisa. 저장된 실제 숫자입니다.
만티 사는 문제의 원인입니다. 그것은 과학적 표기법과 비슷합니다.베이스 2 (이진)의 숫자 만 1.110011 x 2 ^ 5 또는 이와 비슷한 것으로 보입니다. 그러나 바이너리에서 첫 번째 1은 항상 1입니다 (0의 표현 제외)
따라서, 약간의 메모리 공간을 절약하기 위해 (pun 의도 된) IEEE는 1을 가정해야한다고 결정했다. 예를 들어 1011의 mantisa는 실제로 1.1011입니다.
이로 인해 비교 문제가 발생할 수 있습니다. 특히 0은 부동 소수점으로 정확하게 표현할 수 없으므로 특히 0입니다. 이것이 다른 답변으로 설명 된 부동 소수점 수학 문제 외에도 ==가 권장되지 않는 주된 이유입니다.
Java는 다양한 플랫폼에서 언어가 보편적이라는 점에서 고유 한 문제가 있습니다. 각 플랫폼은 고유 한 부동 형식을 가질 수 있습니다. 따라서 ==를 피하는 것이 더욱 중요합니다.
평등에 대해 두 개의 부동 소수점 (언어별로 생각하지 않는)을 비교하는 올바른 방법은 다음과 같습니다.
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
여기서 ACCEPTABLE_ERROR는 #defined 또는 0.000000001과 같은 다른 상수 또는 Victor가 이미 언급했듯이 필요한 정밀도가 있습니다.
일부 언어에는이 기능 또는이 상수가 내장되어 있지만 일반적으로 좋은 습관입니다.
현재로서는 빠르고 쉬운 방법은 다음과 같습니다.
if (Float.compare(sectionID, currentSectionID) == 0) {...}
그러나 문서 는 마진 차이 값 ( 엡실론)을 명확하게 지정하지 않습니다. 는 항상 부동 소수점 계산에 @Victor의 답변 하지는 않지만 표준 언어 라이브러리의 일부이므로 합리적인 것이어야합니다.
그러나 더 높거나 맞춤화 된 정밀도가 필요한 경우
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
또 다른 솔루션 옵션입니다.
(sectionId == currentSectionId)
되어 부동 소수점에 대해 정확하지 않은 것과 같습니다 . 엡실론 방법은이 대답에있는 더 나은 방법입니다 : stackoverflow.com/a/1088271/4212710
반올림 오류로 인해 시작점 값이 신뢰할 수 없습니다.
따라서 sectionID와 같은 키 값으로 사용해서는 안됩니다. 대신 정수를 사용하거나 가능한 값이 충분하지 않은 long
경우 정수를 사용하십시오 int
.
double
s는 훨씬 정확하지만 부동 소수점 값이기도하므로 내 대답은 float
및을 모두 포함해야합니다 double
.
이전 답변 외에도 다음과 관련된 이상한 행동이 있음을 알고 있어야합니다. -0.0f
하고 +0.0f
(그들이 ==
아니라 equals
)과 Float.NaN
(그것은이다 equals
하지만==
) (희망 나는 그 권리있어! - 아아, 그것을하지 않습니다).
편집 : 확인하자!
import static java.lang.Float.NaN;
public class Fl {
public static void main(String[] args) {
System.err.println( -0.0f == 0.0f); // true
System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false
System.err.println( NaN == NaN); // false
System.err.println(new Float( NaN).equals(new Float( NaN))); // true
}
}
IEEE / 754에 오신 것을 환영합니다.
==
한다고해서 숫자가 "비트와 동일"하다는 의미는 아닙니다 (동일한 숫자는 다른 비트 패턴으로 표시 될 수 있지만 그 중 하나만 정규화 된 형식 임). 물론, -0.0f
그리고 0.0f
상이한 비트 패턴 (부호 비트가 상이하다)으로 표시되지만와 동일하게 비교된다 ==
(그러나 함께 equals
). ==
비트 비교에 대한 귀하의 가정 은 일반적으로 말하면 잘못입니다.
여기에 발생할 수있는이 문제와 다른 많은 부동 소수점 문제에 대한 매우 긴 (그러나 희망적으로 유용한) 토론이 있습니다. 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 사항
Float.floatToIntBits ()를 사용할 수 있습니다.
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
다음은 최상의 정밀도를 자동으로 사용합니다.
/**
* Compare to floats for (almost) equality. Will check whether they are
* at most 5 ULP apart.
*/
public static boolean isFloatingEqual(float v1, float v2) {
if (v1 == v2)
return true;
float absoluteDifference = Math.abs(v1 - v2);
float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2));
return absoluteDifference < 5 * maxUlp;
}
물론, 5 개 이상의 ULP ( '마지막 장소')를 선택할 수 있습니다.
아파치 커먼즈 라이브러리에있어 경우, Precision
클래스가 compareTo()
와 equals()
엡실론과 ULP 모두.
double
합니다.
동일한 실수를 생성하는 두 개의 다른 계산이 반드시 동일한 부동 소수점 숫자를 생성하지는 않습니다. 계산 결과를 비교하기 위해 ==를 사용하는 사람들은 일반적으로 이것에 놀라게되므로 경고는 미묘하고 버그를 재현하기 어려운 것을 표시하는 데 도움이됩니다.
다른 답변에서 언급했듯이 복식에는 작은 편차가있을 수 있습니다. 또한 "허용 가능한"편차를 사용하여 비교할 고유 한 방법을 작성할 수 있습니다. 그러나 ...
복식 비교를위한 아파치 클래스가 있습니다 : org.apache.commons.math3.util.Precision
그것은 몇 가지 흥미로운 상수를 포함 SAFE_MIN
하고EPSILON
간단한 산술 연산의 가능한 최대 편차입니다.
또한 복식, 동등 또는 반올림을 비교하는 데 필요한 방법을 제공합니다. (ulps 또는 절대 편차 사용)
한 줄로 대답하면 다음을 사용해야합니다.
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
관련 연산자를 올바르게 사용하는 방법에 대해 더 배우기 위해 여기에서 몇 가지 사례를 자세히 설명합니다. 일반적으로 Java에서 문자열을 테스트하는 세 가지 방법이 있습니다. ==, .equals () 또는 Objects.equals ()를 사용할 수 있습니다.
그것들은 어떻게 다릅니 까? == 문자열에서 참조 품질을 테스트하여 두 개체가 동일한 지 여부를 확인합니다. 반면에 .equals ()는 두 문자열이 논리적으로 동일한 값인지 여부를 테스트합니다. 마지막으로 Objects.equals ()는 두 문자열의 null을 테스트 한 다음 .equals ()를 호출할지 여부를 결정합니다.
사용하기에 이상적인 연산자
세 사업자는 각각 고유 한 강점과 약점을 가지고 있기 때문에 이것은 많은 논쟁의 대상이되었습니다. 예를 들어, ==는 종종 객체 참조를 비교할 때 선호되는 옵션이지만 문자열 값도 비교하는 것처럼 보일 수 있습니다.
그러나 Java는 가치를 비교하고 있지만 실제로는 그렇지 않다는 환상을 만들어 내기 때문에 하락 가치입니다. 아래 두 가지 경우를 고려하십시오.
String a="Test";
String b="Test";
if(a==b) ===> true
String nullString1 = null;
String nullString2 = null;
//evaluates to true
nullString1 == nullString2;
//throws an exception
nullString1.equals(nullString2);
따라서 설계된 특정 속성을 테스트 할 때 각 연산자를 사용하는 것이 좋습니다. 그러나 거의 모든 경우에 Objects.equals ()는보다 보편적 인 연산자이므로 웹 개발자가이를 선택하는 경험이 있습니다.
여기에서 자세한 정보를 얻을 수 있습니다 : http://fluentthemes.com/use-compare-strings-java/
올바른 방법은
java.lang.Float.compare(float1, float2)
Float.compare(1.1 + 2.2, 3.3) != 0