Java에서 ==를 사용하여 플로트를 비교하는 것이 잘못된 점은 무엇입니까?
이 java.sun 페이지에 따르면 ==
는 Java에서의 부동소수점 숫자의 등가비교 연산자입니다.
단, 이 코드를 입력하면 다음과 같습니다.
if(sectionID == currentSectionID)
편집기로 이동하여 정적 분석을 실행하면 "JAVA0078 부동소수점 값과 == 비교"라는 메시지가 나타납니다.
를 사용하는 데 어떤 문제가 있습니까?==
동동 소소 을을 을을 교? ???요?
플로트의 '충격'을 테스트하는 올바른 방법은 다음과 같습니다.
if(Math.abs(sectionID - currentSectionID) < epsilon)
여기서 엡실론은 원하는 정밀도에 따라 0.00000001과 같은 매우 작은 숫자입니다.
부동소수점 값은 조금 꺼질 수 있으므로 정확하게 동일하다고 보고되지 않을 수 있습니다.예를 들어 플로트를 "6.1"로 설정한 다음 다시 인쇄하면 "6.09999904632568359375"와 같은 값이 보고될 수 있습니다.이는 플로트의 작동 방식에 기본적이므로 등식을 사용하여 비교하는 것이 아니라 범위 내에서 비교하는 것이 좋습니다. 즉, 플로트와 플로트의 비교하려는 수의 차이가 특정 절대값보다 작을 경우입니다.
등록부에 기재되어 있는 이 기사는 왜 이런 일이 일어나는지에 대한 좋은 개요를 제공합니다; 유용하고 흥미로운 읽을거리입니다.
다른 사람들이 말하는 뒤에 있는 이유를 말하려고요.
플로트의 바이너리 표현은 좀 거슬리네요.
이진법에서 대부분의 프로그래머는 1b=1d, 10b=2d, 100b=4d, 1000b=8d 사이의 상관관계를 알고 있습니다.
음, 그건 다른 방식으로도 작용합니다.
.1b=.5d, .01b=.25d, .001b=.125, ...
문제는 0.1,.2,.3 등 대부분의 10진수를 나타내는 정확한 방법이 없다는 것입니다.당신이 할 수 있는 것은 대략적인 이진수입니다.100000000001 또는 .99999999999가 아닌 .1이 표시되도록 숫자가 인쇄되면 시스템은 약간 퍼지 라운딩을 합니다(아마도 .1과 같이 저장된 표현과 비슷합니다).
코멘트에서 편집:이것이 문제가 되는 이유는 우리의 기대 때문입니다.2/3을 10진수로 변환할 때 어느 시점에서 완전히 퍼지될 것으로 예상합니다.7, .67, .66667 중 하나입니다.단, .1이 자동적으로 2/3와 같은 방법으로 반올림 되는 것은 아닙니다.그것이 바로 지금 일어나고 있는 일입니다.
덧붙여서, 내부에 격납되어 있는 숫자는, 바이너리 「과학적 표기법」을 사용한 순수 바이너리 표현입니다.따라서 10진수 10.75d를 저장하도록 지시하면 10진수 1010b, 10진수 0.11b가 저장됩니다.따라서 0.101011을 저장하고 마지막에 소수점 4자리를 오른쪽으로 이동합니다.라고 말하기 위해 몇 비트를 저장합니다.
(기술적으로는 소수점이 아니지만 지금은 2진수점이지만, 이 용어로 인해 이 답이 유용하다고 생각하는 대부분의 사람들이 이해하기 쉬워지지는 않았을 것입니다.)
==를 사용하여 부동소수점 값을 비교하는 데 어떤 문제가 있습니까?
0.1 + 0.2 == 0.3
플로트(복식)는 혼란이 많다고 생각합니다만, 정리하는 것이 좋습니다.
표준 준거 JVM [*]에서 플로트를 ID로 사용해도 본질적으로 문제될 것은 없습니다.플로트 ID를 x로 설정하고 아무 것도 하지 않고(즉, 산술 없이) 나중에 y == x에 대해 테스트하면 문제가 없습니다.HashMap에서 키로 사용해도 문제가 없습니다.당신이 할 수 없는 것은 다음과 같은 평등을 가정하는 것이다.
x == (x - y) + y
예를 들면, ID로서 정수형을 사용하는 경우가 많아, 대부분의 사람이 이 코드로 미루고 있는 것을 알 수 있기 때문에, 실제적인 이유에서는, 통념에 따르는 것이 좋다.한 것들이 바랍니다.double
이 길기values
때문에 수 없습니다.double
「 ID의 생성은,의 배수로 이 있어, 부동 산술 「next available ID」, 「next available ID」, 「next available ID」, 「next available ID」의 합니다.수고할 가치가 없다.반면에, 두 수학적으로 동등한 계산 결과의 수치적 동일성에 의존하는 것은 위험하다.이는 반올림 오류와 십진표현에서 이진표현으로 변환할 때의 정밀도가 떨어지기 때문입니다.이것은 SO에 대해 죽도록 논의되어 왔다.
[*] '표준 준거 JVM'이라고 했을 때 뇌에 손상을 입은 특정 JVM 구현을 제외하려고 했습니다.이것 좀 봐.
현시점에서는, 다음과 같이 간단하게 실시할 수 있습니다.
if (Float.compare(sectionID, currentSectionID) == 0) {...}
단, 이 문서에서는 플로트에 대한 계산에 항상 존재하는 여백 차이(@Victor의 답변에서 나온 엡실론)의 값을 명확하게 명시하고 있지는 않지만 표준 언어 라이브러리의 일부이므로 합리적인 값이어야 합니다.
그러나 더 높은 정밀도 또는 맞춤형 정밀도가 필요한 경우
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
다른 솔루션 옵션입니다.
반올림 오류로 인해 포팅 포인트 값을 신뢰할 수 없습니다.
은 section ,, 、 as as 。 대신 ID를 합니다. 대신 정수를 사용합니다.long
int
가능한 값이 충분하지 않습니다.
이것은 Java만의 문제가 아닙니다.==를 사용하여 두 개의 부동/소수/소수 유형 숫자를 비교하는 것은 저장 방식 때문에 잠재적으로 문제를 일으킬 수 있습니다.단정도 플로트(IEEE 표준 754에 준거)에는 32비트가 있으며 다음과 같이 분배됩니다.
비트 - =1=1 트 - 명(0 = =, 1 = 음)
비트 - x 한 (8' - '(2^x' x' - 'bias-127' (bias-127'
23번 - 23번입니다.저장된 실제 모든 번호입니다.
사마귀가 문제의 원인입니다.과학적 표기법과 비슷하지만, 2진수(이진수)의 숫자만 1.110011 x 2^5처럼 보입니다.그러나 이진수에서는 첫 번째 1은 항상 1입니다(0의 표현 제외).
따라서 IEEE에서는 메모리 공간을 조금 절약하기 위해 (펀이 의도된)1을 상정할 필요가 있다고 판단했습니다.예를 들어, 1011의 사마귀는 1.1011입니다.
이 경우 0을 플로트로 정확하게 나타낼 수 없기 때문에 특히 0과 비교할 때 문제가 발생할 수 있습니다.이것이 ==가 권장되지 않는 주된 이유이며, 다른 답변에서 설명한 부동 소수점 산술 문제도 이에 해당됩니다.
Java는 언어가 여러 플랫폼에 걸쳐 보편적이라는 점에서 독특한 문제를 안고 있으며, 각 플랫폼마다 고유한 플로트 형식이 있을 수 있습니다.따라서 ==를 피하는 것이 더욱 중요합니다.
2개의 플로트(언어 고유의 마인드 유저가 아닌)를 동등하게 비교하는 적절한 방법은 다음과 같습니다.
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
여기서 ACCEPTABLE_ERROR은 #defined이거나 Victor가 이미 언급한 바와 같이 0.0000001과 동일한 기타 정수 또는 필요한 정밀도입니다.
일부 언어에는 이 기능이나 상수 기능이 내장되어 있지만, 일반적으로 이것은 좋은 습관입니다.
말한 대답 에, '아까보다'와 한 행동들이 .-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에 오신 것을 환영합니다.
이 문제 및 기타 발생할 수 있는 부동소수점 문제에 대한 매우 긴(그러나 도움이 되는) 논의를 다음에 나타냅니다.부동 소수점 산술에 대해 컴퓨터 과학자가 알아야 할 사항
일단 플로트인가요 플로트인가요?그 중 하나가 Float일 경우 equals() 메서드를 사용해야 합니다.또한 static Float.compare 메서드를 사용하는 것이 좋습니다.
플로트를 사용할 수 있습니다.floatToIntBits() 입니다.
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
==이길 원할 수 있지만 123.444444444444443!= 123.44444444444442
플로트를 사용해야 하는 경우 strictfp 키워드를 사용하면 편리합니다.
http://en.wikipedia.org/wiki/strictfp
다음은 자동으로 최상의 정밀도를 사용합니다.
/**
* 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('마지막 위치에 있는 단위')를 선택할 수도 있습니다.
Apache Commons Precision
에 '''가 있습니다.compareTo()
★★★★★★★★★★★★★★★★★」equals()
ULP를 사용하다
동일한 실수를 산출하는 두 가지 다른 계산이 반드시 동일한 부동 소수점 수를 산출하는 것은 아닙니다.==를 사용하여 계산 결과를 비교하는 사람들은 대개 이 사실에 놀라게 되므로, 경고를 통해 미묘하고 재현하기 어려운 버그를 경고할 수 있습니다.
섹션이라는 이름의 물건에 플로트를 사용하는 아웃소싱 코드를 취급하고 있습니까?ID 및 현재 섹션신분증? 궁금해서.
@Bill K: "플로트의 바이너리 표현은 좀 짜증나요."어떻게요?어떻게 하면 더 잘할 수 있을까요?어떤 베이스에서도 올바르게 나타낼 수 없는 숫자가 있습니다.왜냐하면 그것들은 끝이 없기 때문입니다.파이가 좋은 예입니다.근사치밖에 몰라요.보다 좋은 솔루션이 있는 경우는, 인텔에 문의해 주세요.
다른 답변에서 언급했듯이, 복식은 작은 편차를 가질 수 있습니다.또한 "허용 가능한" 편차를 사용하여 비교할 수 있는 고유한 방법을 작성할 수 있습니다.하지만...
org.apache.commons.math3.util의 복식을 비교하는 아파치 클래스가 있습니다.정밀도
여기에는 몇 가지 흥미로운 상수가 포함되어 있습니다.SAFE_MIN
★★★★★★★★★★★★★★★★★」EPSILON
단순한 산술 연산의 가능한 최대 편차입니다.
또한 동일한 복식 또는 라운드 복식 비교에 필요한 방법도 제공합니다.(궤양 또는 절대편차 사용)
한마디로 다음과 같이 대답할 수 있습니다.
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
관련 연산자의 올바른 사용에 대해 더 많이 배울 수 있도록 몇 가지 사례를 설명하겠습니다. Java에서는 일반적으로 3가지 방법으로 문자열을 테스트할 수 있습니다.==, .timeout() 또는 Objects.equals()를 사용할 수 있습니다.
어떻게 다른가요?== 두 물체가 동일한지 여부를 확인하는 문자열로 기준 품질을 테스트합니다.한편, .equals()에서는 두 문자열의 값이 논리적으로 동일한지 여부를 테스트합니다.마지막으로 Objects.equals()는 두 문자열의 늘을 테스트한 후 .equals()를 호출할지 여부를 결정합니다.
사용하기에 이상적인 연산자
이것은 많은 논쟁의 대상이 되어 왔습니다.왜냐하면 3개의 연산자 각각은 각각 고유한 강점과 약점을 가지고 있기 때문입니다.예를 들어 객체 참조를 비교할 때 ==가 선호되는 경우가 많지만 문자열 값을 비교하는 것처럼 보이는 경우도 있습니다.
그러나 Java는 사용자가 값을 비교하고 있다는 착각을 일으키지만 실제로는 그렇지 않기 때문에 얻을 수 있는 값은 폴스 값입니다.다음 두 가지 경우를 고려하십시오.
케이스 1:
String a="Test";
String b="Test";
if(a==b) ===> true
케이스 2:
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)
반올림 오차를 줄이는 한 가지 방법은 부동보다는 이중을 사용하는 것입니다.이것이 문제를 해결하지는 못하지만, 프로그램의 오류 수를 줄여주므로 부동하는 것이 가장 좋은 선택이라고는 것은 거의 없습니다.IMHO.
언급URL : https://stackoverflow.com/questions/1088216/whats-wrong-with-using-to-compare-floats-in-java
'source' 카테고리의 다른 글
VueJ를 사용하여 자식 속성을 동적으로 정의하여 목록 내 레코드 편집s (0) | 2022.08.12 |
---|---|
beforeRouterEnter 가드에서 next()를 호출하는 방법 (0) | 2022.08.12 |
C의 부울 값 사용 (0) | 2022.08.11 |
makefile의 CXX 값은 어디에서 얻을 수 있습니까? (0) | 2022.08.11 |
Nuxt/Vue 반응체 속성 (0) | 2022.08.11 |