편집 :이 질문은 두 가지 주제를 다룹니다.
float 대신 Java double을 항상 사용해서는 안되는 이유가 있습니까?
float를 사용할 때이 테스트 코드가 실패하고 유일한 차이점이 double 대신 float를 사용하는 이유가 명확하지 않기 때문에이 질문을합니다.
public class BigDecimalTest {
@Test public void testDeltaUsingDouble() { //test passes
BigDecimal left = new BigDecimal("0.99").setScale(2,BigDecimal.ROUND_DOWN);
BigDecimal right = new BigDecimal("0.979").setScale(2,BigDecimal.ROUND_DOWN);
Assert.assertEquals(left.doubleValue(), right.doubleValue(), 0.09);
Assert.assertEquals(left.doubleValue(), right.doubleValue(), 0.03);
Assert.assertNotEquals(left.doubleValue(), right.doubleValue(), 0.02);
Assert.assertNotEquals(left.doubleValue(), right.doubleValue(), 0.01);
Assert.assertNotEquals(left.doubleValue(), right.doubleValue(), 0.0);
}
@Test public void testDeltaUsingFloat() { //test fails on 'failing assert'
BigDecimal left = new BigDecimal("0.99").setScale(2,BigDecimal.ROUND_DOWN);
BigDecimal right = new BigDecimal("0.979").setScale(2,BigDecimal.ROUND_DOWN);
Assert.assertEquals(left.floatValue(), right.floatValue(), 0.09);
Assert.assertEquals(left.floatValue(), right.floatValue(), 0.03);
/* failing assert */ Assert.assertNotEquals(left.floatValue() + " - " + right.floatValue() + " = " + (left.floatValue() - right.floatValue()),left.floatValue(), right.floatValue(), 0.02);
Assert.assertNotEquals(left.floatValue(), right.floatValue(), 0.01);
Assert.assertNotEquals(left.floatValue(), right.floatValue(), 0.0);
}}
실패 메시지 :
java.lang.AssertionError: 0.99 - 0.97 = 0.01999998. Actual: 0.9900000095367432
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failEquals(Assert.java:185)
at org.junit.Assert.assertNotEquals(Assert.java:230)
at com.icode.common.BigDecimalTest.testDeltaUsingFloat(BigDecimalTest.java:34)
이 테스트가 실패한 이유와 왜 항상 float 대신 double을 사용해서는 안되는지 아십니까? 물론 double 이외의 이유는 float보다 넓습니다.
편집 : 재미있는 점은 Assert.assertNotEquals (double, double, delta)가 두 경우 모두 두 배를 취하므로 실패한 테스트에서 반환 된 수레가 어쨌든 두 배로 넓어지고 왜 테스트 실패가 발생합니까?
편집 :이 다른 질문이 관련이있을 수 있지만 확실 하지 않습니다 .16 진수가 동일하지 않습니다.
편집 :이 질문에 대한 대답에서 16 진수는 동일하지 않습니다. float에 대한 .99에 대한 과학적 표현 IEEE 754는 동일한 값에 대해 double과 다르다는 결론을 내릴 수 있습니다. 이것은 반올림 때문입니다.
따라서 우리는 이것을 얻습니다.
위의 단위 테스트에서 최대 델타는 0.02이고 (실패한 테스트에서) 0.01999998은 델타 값보다 낮기 때문에 숫자는 동일하지만 테스트에서는 실패하지 않는다고 주장합니다.
이 모든 것에 동의하십니까?
BigDecimal에 대한 문서 는 floatValue()
반올림 방법에 대해 침묵합니다 . 나는 그것이 가장 가깝게, 짝수로 동점을 사용한다고 가정합니다.
left
그리고 right
각각 0.99와 0.97로 설정됩니다. double
가장 가까운 모드 로 변환 하면 결과는 0.9899999999999999911182158029987476766109466552734375 (16 진수 부동 소수점, 0x1.fae147aep-1) 및 0.9699999999999999733546474089962430298328399658203125 (0x1.f0a3d70a3d70ap-1)입니다. 이 값을 빼면 결과는 0.020000000000000017763568394002504646778106689453125로, .02를 분명히 초과합니다.
.99 및 .97을로 변환 float
하면 결과는 0.9900000095367431640625 (0x1.fae148p-1) 및 0.9700000286102294921875 (0x1.f0a3d8p-1)입니다. 이 값을 빼면 결과는 0.019999980926513671875로 .02보다 분명히 작습니다.
간단히 말해, 10 진수가 부동 소수점으로 변환 될 때 반올림은 위 또는 아래 일 수 있습니다. 가장 가까운 표현 가능한 부동 소수점 값을 기준으로 숫자가 어디에 놓여 있는지에 따라 다릅니다. 제어되거나 분석되지 않으면 사실상 무작위입니다. 따라서 때로는 예상했던 것보다 더 큰 가치로 끝나고 때로는 더 낮은 가치로 끝나기도합니다.
double
대신 사용 한다고해서 위와 유사한 결과 float
가 발생 하지 않는다는 보장은 없습니다. double
이 경우 값이 정확한 수학적 값을 초과했지만 float
값은 그렇지 않은 경우에 불과 합니다. 다른 숫자를 사용하면 반대가 될 수 있습니다. 예를 들어,와 double
, .09-.07
와,보다 0.02이지만, float
.07f`이 0.02보다 크다 - .09f.
Handbook of Floating-Point Arithmetic 과 같이 부동 소수점 산술을 다루는 방법에 대한 많은 정보가 있습니다. Stack Overflow 질문에서 다루기에는 너무 큰 주제입니다. 그것에 대학 과정이 있습니다.
오늘날의 일반적인 프로세서에서는 사용 double
하는 데 드는 추가 비용이 거의 없습니다 float
. 단순 스칼라 부동 소수점 연산은 double
및에 대해 거의 동일한 속도로 수행됩니다 float
. 데이터가 너무 많아서 데이터를 전송하는 시간 (디스크에서 메모리로 또는 메모리에서 프로세서로)이 중요해 지거나 디스크에서 차지하는 공간이 커지거나 소프트웨어가 프로세서의 SIMD 기능을 사용하는 경우 성능 차이가 발생합니다. (SIMD를 사용하면 프로세서가 여러 데이터 조각에 대해 병렬로 동일한 작업을 수행 할 수 있습니다. 현재 프로세서는 일반적으로 float
SIMD 작업에 대해 double
SIMD 작업 에 대해 약 두 배의 대역폭을 제공하거나 double
SIMD 작업을 전혀 제공하지 않습니다 .)
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다