精度を失うことなく、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

ただし、キャストする代わりに、floatを文字列として出力し、文字列をdoubleとして解析すると、必要なものが得られます。

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

文字列に戻って戻るよりも良い方法はありますか?

回答:


124

それはあなたが実際に余分な精度を得ているということではありません-それはフロートがあなたが最初に目指していた数を正確に表していないということです。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を追加することによりdoubleに拡張されているのを見ることができますが、0.27のdouble表現は「より正確」であるため、問題があります。

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000

25

これは、契約によるものであるFloat.toString(float)一部には書かれています:

小数部分には何桁印刷する必要がありますか[…]?小数部を表すには、少なくとも1桁が必要です。それを超えると、引数の値とfloat型の隣接する値を一意に区別するために必要な桁数だけ 増えます。つまり、xが有限の非ゼロの引数fに対してこの方法で生成された10進数表現によって表される正確な数学的値であるとします。次に、fはxに最も近いfloat値でなければなりません。または、2つのfloat値がxに等しく近い場合、fはそれらの1つでなければならず、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
私のために働いた。また、非常に高速です。これが回答としてマークされていない理由がわかりません。
2013年

これは私が使用する方法で、新しいFloatパーツは必要ありません...プリミティブを使用してFloatingDecimalを作成するだけです。それは自動的にボックス化されます...そしてまた速く動作します...欠点があります:FloatingDecimalは不変なので、すべてのフロートに対して1つ作成する必要があります... O(1e10)の計算コードを想像してください! !! もちろんBigDecimalにも同じ欠点があります...
Mostafa Zeinali 2014

6
このソリューションの欠点は、sun.misc.FloatingDecimalがJVMの内部クラスであり、そのコンストラクターのシグニチャーがJava 1.8で変更されていることです。実際のアプリケーションでは、内部クラスを使用するべきではありません。
Igor Bljahhin

7

/のBigDecimal代わりにを使用してください。2進浮動小数点として表現できない数値がたくさんあります(たとえば、)。したがって、常に結果を既知の精度に丸めるか、を使用する必要があります。floatdouble0.1BigDecimal

詳細については、http://en.wikipedia.org/wiki/Floating_pointを参照してください。


キャッシュに1,000万のフロート取引価格があるとしましょう。それでもBigDecimalを使用してユニークなインスタンスを作成することを検討します(フライウェイト/不変を割引くために)〜6mのオブジェクトと言いますか?
Sendi_t

1
@Sendi_tコメントを使用して複雑な質問をしないでください:-)
アーロンディグラ

見てくれてありがとう!.. 10年前に合意されたフロートを今すぐ使用します-この状況についてのフォローアップであなたの視点を確認しようとしていました-。ありがとう!
Sendi_t

@Sendi_t誤解。512文字ではお答えできません。適切な質問をして、リンクを送ってください。
アーロンディグラ

1
@GKFXコンピュータで小数を処理する場合、「1つのサイズですべてに対応」することはできません。しかし、ほとんどの人はそれを理解できないのでBigDecimal、一般的なエラーのほとんどをキャッチできるので、私はそれらを指摘します。物事が遅すぎる場合、彼らはもっと学び、問題を最適化する方法を見つける必要があります。時期尚早の最適化はすべての悪の根源です-DE Knuth。
Aaron Digulla

7

私は次の解決策を見つけました:

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

あなたが使用している場合はfloatをし、倍増するのではなく、フロート及びダブル次のコマンドを使用します。

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

1

フロートは本質的に不正確であり、常に適切な丸めの「問題」があります。精度が重要な場合は、アプリケーションをリファクタリングして、DecimalまたはBigDecimalを使用することを検討してください。

はい、プロセッサのサポートにより、浮動小数点は小数よりも計算が高速です。しかし、あなたは速くまたは正確にしたいですか?


1
10進数の計算も不正確です。(例1/3 * 3 == 0.9999999999999999999999999999)もちろん、お金などの正確な小数を表すのに適していますが、物理的な測定の場合は利点がありません。
dan04 2010

2
しかし、1 == 0.9999999999999999999999999999 :)
Emmanuel Bourg

0

情報については、項目48-Joshua BlochによるEffective Java 2nd editionの項目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.