BigDecimal이 float 또는 double로 정확하게 변환 될 수 있는지 확인하는 방법은 무엇입니까?


10

클래스 BigDecimal에는 무손실 변환을 보장하는 몇 가지 유용한 방법이 있습니다.

  • byteValueExact()
  • shortValueExact()
  • intValueExact()
  • longValueExact()

그러나 방법 floatValueExact()doubleValueExact()존재하지 않습니다.

메소드 floatValue()및에 대한 OpenJDK 소스 코드를 읽었습니다 doubleValue(). 둘 다 Float.parseFloat()및 으로 폴백되는 것처럼 보이며 Double.parseDouble()양 또는 음의 무한대를 반환 할 수 있습니다. 예를 들어 10,000 9s의 문자열을 구문 분석하면 양의 무한대가 반환됩니다. 내가 이해하는 것처럼, BigDecimal무한의 내부 개념이 없습니다. 또한, 100 9s의 문자열을 double제공 1.0E100하는 대로 파싱하면 무한대가 아니지만 정밀도가 떨어집니다.

합리적인 구현 무엇 floatValueExact()doubleValueExact()?

나는 생각 double결합하여 솔루션 BigDecimal.doubleValue(), BigDecial.toString(), Double.parseDouble(String)Double.toString(double),하지만 지저분한 보인다. 더 간단한 해결책이있을 수 있기 때문에 여기에 묻고 싶습니다.

분명히하기 위해 고성능 솔루션이 필요하지 않습니다.


7
더블로 변환 한 다음 더블을 BigDecimal로 다시 변환하고 동일한 값을 얻을 수 있는지 확인하십시오. 유스 케이스가 무엇인지 확실하지 않습니다. 복식을 사용하는 경우 이미 정확하지 않은 값을 갖습니다. 정확한 값을 원하면 BigDecimal을 유지하십시오.
JB 니 제트

답변:


6

에서 읽기 문서를 , 그것은으로 수행하는 모든 변종은 일부 부분의 존재를 확인하는 것입니다 또는 값이 숫자 유형에 비해 너무 크고, 경우에 예외를 throw합니다. numTypeValueExact

에 관해서 floatValue()doubleValue()유사한 오버 플로우 검사 대신 반환, 대신 예외를 던지는 수행되고 Double.POSITIVE_INFINITY또는 Double.NEGATIVE_INFINITY복식과 Float.POSITIVE_INFINITYFloat.NEGATIVE_INFINITY수레를 위해.

따라서 가장 합리적인 (그리고 간단한) 구현 exactfloat과 double 방법은, 간단하게 변환 반환 여부를 확인해야한다 POSITIVE_INFINITYNEGATIVE_INFINITY.


또한 , 그 기억 BigDecimal사용에서 오는 정밀도의 부족으로 처리 할 수 있도록 설계되었다 float또는 double때문에 같은 큰 irrationals 위해를 @JB Nizet가 댓글을 달았습니다 , 당신은 위의에 추가 할 수있는 또 다른 검사는 변환하는 것 double또는 float다시 BigDecimal당신은 여전히 얻을 수 있는지 확인하기 위해 같은 가치. 변환이 올바른지 확인해야합니다.

이러한 방법은 다음과 같습니다 floatValueExact().

public static float floatValueExact(BigDecimal decimal) {
    float result = decimal.floatValue();
    if (!Float.isInfinite(result)) {
        if (new BigDecimal(String.valueOf(result)).compareTo(decimal) == 0) {
            return result;
        }
    }
    throw new ArithmeticException(String.format("%s: Cannot be represented as float", decimal));
}

상기 compareTo대신에 의 사용은 equals체크에 너무 엄격하지 않도록 의도적 인 것이다. equalsBigDecimal객체의 값과 스케일이 동일한 경우에만 참으로 평가되며 (소수의 소수 부분 크기) compareTo중요하지 않은 경우이 차이를 간과합니다. 예를 들어 2.02.00.


매우 교활합니다. 정확히 내가 필요한 것! 참고 : 또한 사용하실 수 있습니다 Float.isFinite()또는 Float.isInfinite(), 그러나 이것은 선택 사항입니다. :)
kevinarpe '

3
BigDecimal의 equals ()는 2.0과 2.00을 다른 값으로 간주하므로 compareTo와 equals를 선호해야합니다.
JB 니 제트

float로 정확하게 표현할 수없는 값은 작동하지 않습니다. 예 : 123.456f. 나는 이것이 32 비트 float와 64-bit double 사이의 다른 크기 (mantissa)의 크기 때문이라고 생각합니다. 위의 코드에서 다음과 같은 결과가 더 좋습니다 if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {. 이것이 합리적인 변화입니까? 아니면 부동 소수점 값의 또 다른 코너 케이스가 누락 되었습니까?
kevinarpe '12

1
@kevinarpe- 123.456f개념적으로 1.0E100 예제와 동일합니다. 그것은 나 파업 당신이 경우 필요 BigDecimal의에서 변환하는지 확인 -> 이진 부동 소수점 정확한 다음입니다 필요가 있다는 문제입니다; 즉, 변환이 고려되지 않아야한다.
Stephen C
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.