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


97

나는 기본 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으로 다시 돌아가는 것보다 더 좋은 방법이 있습니까?

답변:


125

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

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

float f = 0.1F;
double d = f;

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

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

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


40

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

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

25

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

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


13

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

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

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

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

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

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


1
나를 위해 일했습니다. 또한 매우 빠릅니다. 이것이 답변으로 표시되지 않은 이유를 잘 모르겠습니다.
Sameer 2013 년

이것은 내가 사용하는 방법이며 새로운 Float 부분이 필요하지 않습니다. 기본 요소로 FloatingDecimal을 만드십시오. 그것은 자동으로 boxed 될 것입니다 ... 그리고 빠르게 작동합니다 ... 하나의 단점이 있습니다. FloatingDecimal은 불변이므로 모든 단일 부동 소수점에 대해 하나씩 만들어야합니다 ... O (1e10)의 계산 코드를 상상해보십시오! !! 물론 BigDecimal와 ... 같은 단점이있다
모스 타파 Zeinali

6
이 솔루션의 단점은 sun.misc.FloatingDecimal이 JVM의 내부 클래스이고 생성자의 서명이 Java 1.8에서 변경되었다는 것입니다. 아무도 실제 응용 프로그램에서 내부 클래스를 사용해서는 안됩니다.
Igor Bljahhin 2015

8

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

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();
}

7

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

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


캐시에 10m 부동 거래 가격이 있다고 가정 해 봅시다. 여전히 BigDecimal을 사용하고 고유 한 인스턴스를 생성하여 ~ 6m 개체를 인스턴스화 할 수 있습니까 (flyweight / immutable 할인을 위해) .. 아니면 더 많은 것이 있습니까?
Sendi_t 2015 년

1
@Sendi_t 복잡한 질문을하기 위해 댓글을 사용하지 마세요 :-)
Aaron Digulla

찾아 주셔서 감사합니다! .. 우리는 10 년 전에 합의 된대로 지금 당장 수레를 사용하고 있습니다.-후속 조치에서이 상황에 대한 귀하의 관점을 보려고했습니다-. 감사!
Sendi_t

@Sendi_t 오해. 512 자로 된 질문에 답할 수 없습니다. 적절한 질문을하고 링크를 보내주세요.
Aaron Digulla

1
@GKFX 컴퓨터에서 소수를 처리 할 때 "하나의 크기가 모든 것에 적합"하다는 것은 없습니다. 그러나 대부분의 사람들은 이해하지 못하기 BigDecimal때문에 대부분의 일반적인 오류를 잡을 수 있기 때문에 지적합니다. 상황이 너무 느리면 더 많이 배우고 문제를 최적화하는 방법을 찾아야합니다. 조기 최적화는 모든 악의 근원 인 DE Knuth입니다.
Aaron Digulla

1

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

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


1
십진수 산술도 정확하지 않습니다. (예 : 1/3 * 3 == 0.9999999999999999999999999999) 물론 돈과 같은 정확한 십진수 를 표현하는 것이 더 좋지만 물리적 측정에는 이점이 없습니다.
dan04

2
하지만 1 == 0.9999999999999999999999999999 :)
Emmanuel Bourg

0

자세한 내용은 Joshua Bloch의 Effective Java 2nd edition의 Item 48-정확한 값이 필요한 경우 float 및 double을 사용하지 마십시오. 이 책은 좋은 것들로 가득 차 있고 확실히 볼만한 가치가 있습니다.


0

작동합니까?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;

0

잘 작동하는 간단한 해결책은 float의 문자열 표현에서 double을 구문 분석하는 것입니다.

double val = Double.valueOf(String.valueOf(yourFloat));

매우 효율적이지는 않지만 작동합니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.