[java] 정밀도를 잃지 않고 float를 double로 변환

나는 기본 float가 있고 기본 double이 필요합니다. 단순히 플로트를 두 배로 캐스팅하면 이상한 추가 정밀도가 제공됩니다. 예를 들면 :

float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375

그러나 캐스팅 대신 부동 소수점을 문자열로 출력하고 문자열을 double로 구문 분석하면 원하는 것을 얻습니다.

System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35

String으로 다시 돌아가는 것보다 더 좋은 방법이 있습니까?



답변

그것은 당신이 있다는 것을 아니다 실제로 여분의 정밀도를 얻기 – 그것은 플로트 정확하게 원래 목표로 한 수를 나타냅니다하지 않았다입니다. double 원래 플로트를 정확하게 나타냅니다.toString이미 존재하는 “추가”데이터를 표시합니다.

예를 들어 (그리고이 숫자는 옳지 않습니다. 저는 단지 구성하는 중입니다) 다음과 같이 가정 해보십시오.

float f = 0.1F;
double d = f;

그러면의 값은 f정확히 0.100000234523이 될 수 있습니다. d정확히 같은 값을 가지지 만 문자열로 변환하면 더 높은 정밀도로 정확하다고 “신뢰”할 수 있으므로 일찍 반올림하지 않고 이미 있던 “추가 자릿수”를 볼 수 있습니다. 거기에 있지만 당신에게 숨겨져 있습니다.

문자열로 변환했다가 다시 되돌릴 때 원래 float보다 문자열 값에 더 가까운 double 값으로 끝나지만 문자열 값이 실제로 원하는 값이라고 정말로 믿는 경우 에만 좋습니다 .

대신 float / double이 여기에서 사용하기에 적합한 유형 BigDecimal입니까? 정확한 10 진수 값 (예 : 돈)을 가진 숫자를 사용하려는 경우 BigDecimal더 적절한 유형의 IMO가 있습니다.


답변

이진 표현으로 변환하면이 문제를 더 쉽게 이해할 수 있습니다.

float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;

System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));

끝에 0을 추가하여 float가 double로 확장되는 것을 볼 수 있지만 0.27의 이중 표현은 ‘더 정확’하므로 문제가 발생합니다.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000


답변

이 계약으로 인해입니다 Float.toString(float), 일부 말한다 :

소수 부분 […]에 대해 몇 자릿수를 인쇄해야합니까? 소수 부분을 나타내려면 최소한 하나의 숫자가 있어야하며 ,
그 이상에는 인수 값을 float 유형의 인접한 값과 고유하게 구별하는 데 필요한만큼만 더 많은 숫자가 있어야합니다.즉, x가 0이 아닌 유한 인수 f에 대해이 방법으로 생성 된 10 진수 표현으로 표현되는 정확한 수학적 값이라고 가정합니다. 그러면 f는 x에 가장 가까운 부동 소수점 값이어야합니다. 또는 두 개의 float 값이 x에 똑같이 가까우면 f는 그 중 하나 여야하며 f의 최하위 비트는 0이어야합니다.


답변

오늘이 문제가 발생했으며 프로젝트가 정말 거대하기 때문에 BigDecimal에 대한 리팩터링을 사용할 수 없습니다. 그러나 나는

Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()

그리고 이것은 작동합니다.

result.doubleValue ()를 호출하면 5623.22998046875가 반환됩니다.

그러나 doubleResult.doubleValue ()를 호출하면 5623.23이 올바르게 반환됩니다.

그러나 나는 그것이 올바른 해결책인지 완전히 확신하지 못합니다.


답변

다음 해결책을 찾았습니다.

public static Double getFloatAsDouble(Float fValue) {
    return Double.valueOf(fValue.toString());
}

당신이 사용하는 경우 플로트를 하고 두 번 대신 플로트두 번 다음을 사용 :

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}


답변

/ BigDecimal대신 사용하십시오 . 이진 부동 소수점으로 표현할 수없는 숫자가 많이 있습니다 (예 🙂 . 따라서 항상 결과를 알려진 정밀도로 반올림하거나 .floatdouble0.1BigDecimal

자세한 내용은 http://en.wikipedia.org/wiki/Floating_point 를 참조하십시오.


답변

수레는 본질적으로 부정확하며 항상 깔끔한 반올림 “문제”가 있습니다. 정밀도가 중요한 경우 Decimal 또는 BigDecimal을 사용하도록 애플리케이션을 리팩토링하는 것을 고려할 수 있습니다.

예, 부동 소수점은 프로세서 지원으로 인해 소수보다 계산 속도가 빠릅니다. 그러나 빠르고 정확한 것을 원하십니까?