代わりにdoubleが主に推奨されるのに、なぜfloatがJava言語の一部であるのはなぜですか?


84

私が見たすべての場所で、それはほぼすべての点doubleで優れていると言いfloatます。Javaでfloat廃止さdoubleれたのに、なぜまだ使用されているのですか?

私はLibgdxで多くのプログラムを作成しており、それらを使用することを強制していますfloat(deltaTimeなど)が、doubleストレージとメモリの点で作業する方が簡単だと思われます。

私はまた、いつfloatを使用し、doubleを使用するのかを読んでいますが、float本当に小数点以下の桁数が多い数値にのみ適している場合、なぜ多くのバリエーションの1つを使用できないのdoubleですか?

もはや利点がなくなっても、人々がフロートを使用することを主張する理由はありますか?すべてを変更するのは大変な作業ですか?



58
その質問への回答から、「浮動小数点数は、小数点以下の桁数が多い数値にのみ有効である」と、どのように推測しましたか?!彼らは正反対を言います!
オーダス

20
@Eames「数字」ではなく「数字」の意味に注意してください。浮動小数点数は、精度または範囲が必要な場合は悪化し、大量のそれほど正確でないデータが必要場合は向上します。それがその答えです。
オーダス

29
なぜ私たちはbyteshortそしてintいつそこにいるのlongですか?
user253751

15
もっとふさわしい質問は、「何十年もコードが何の理由もなく壊れてしまうような言語から、なぜキーワードとプリミティブデータ型を削除するのか」です。
サラ

回答:


169

LibGDXは、主にゲーム開発に使用されるフレームワークです。

ゲーム開発では、通常、リアルタイムで大量の数値演算を実行する必要があり、パフォーマンスは重要です。そのため、ゲーム開発者は通常、float精度で十分な場合に常にfloatを使用します。

この場合に考慮する必要があるのは、CPUのFPUレジスタのサイズだけではありません。実際、ゲーム開発における大量の演算処理のほとんどはGPUによって実行され、GPU は通常doubleではなくfloatに最適化されています。

そして、次もあります。

  • メモリバス帯域幅(RAM、CPU、GPU間でデータをシャベルできる速度)
  • CPUキャッシュ(これにより、以前の必要性が低くなります)
  • VRAM

これらはすべて、64ビットdoubleではなく32ビットfloatを使用すると2倍の貴重なリソースです。


2
ありがとうございました!メモリ使用量の変化とその理由
イームズ

7
また、SIMD操作の場合、32ビット値のスループットは2倍になります。以下のよう8bittreeの答えは指摘し、GPUは倍精度でさらに大きなパフォーマンスの低下を持っています。
ポールA.クレイトン

5
多くのグラフィックパイプラインは、精度が十分な場合にパフォーマンスを向上させるために16ビットの半浮動小数点数をサポートします。
アディシャビット

22
@phresnelすべてです。位置を移動し、データを更新する必要があります。そして、これは簡単な部分です。次に、テクスチャ、距離をレンダリング(=読み取り、回転、拡大縮小、および変換)し、スクリーンフォーマットに変換する必要があります...やることがたくさんあります。
Sebb

8
@phresnelは、ゲーム開発企業の元VPオペレーションとして、ほぼすべてのゲームで膨大な数の処理が行われることを保証します。通常はライブラリに含まれており、エンジニアから100%抽象化されていることに注意してください。マジック逆平方根、誰か?
corsiKa

57

フロートは、ダブルの半分のメモリを使用します。

それらはdoubleよりも精度が低い場合がありますが、多くのアプリケーションは精度を必要としません。これらは、同様のサイズの固定小数点形式よりも範囲が広くなっています。したがって、それらは、広い範囲の数値を必要とするが、高精度を必要とせず、メモリ使用量が重要なニッチを満たします。たとえば、過去に大規模なニューラルネットワークシステムに使用しました。

Javaの外に移動して、3Dグラフィックスでも広く使用されています。多くのGPUがプライマリフォーマットとして使用しているためです-非常に高価なNVIDIA Tesla / AMD FireProデバイス以外では、GPUの倍精度浮動小数点は非常に遅いです。


8
ニューラルネットワークと言えば、CUDAは現在、機械学習作業用のアクセラレーターの使用が増加しているため、半精度(16ビット)浮動小数点変数をサポートしていますが、精度はさらに低くなりますが、メモリフットプリントはさらに小さくなります。
JAB

FPGAをプログラムするときは、毎回仮数と指数の両方のビット量を手動で選択する傾向があります
。v– Sebi

48

下位互換性

これが、既存の言語/ライブラリ/ ISA / etcで動作を維持する最大の理由です。

Javaからフロートを取り出したらどうなるかを考えてください。Libgdx(および他の数千のライブラリとプログラム)は機能しません。すべてを更新するには多くの労力が必要になります。多くのプロジェクトではおそらく何年もかかるでしょう(後方互換性を破るPython 2からPython 3への移行を見てください)。そして、すべてが更新されるわけではなく、メンテナーがそれらを放棄したために、おそらく更新するよりも手間がかかるため、またはソフトウェアが想定されていたものを達成することができなくなったために、いくつかのものが永遠に壊れますする。

性能

64ビットの倍精度はメモリの2倍の時間がかかり、ほとんどの場合32ビットの浮動小数点よりも処理速度が遅くなります(非常にまれな例外は、32ビットの浮動小数点機能がほとんどまたはまったく使用されないため、最適化するための努力が行われない場合です) 。特殊なハードウェア用に開発しているのでない限り、近い将来これを経験することはありません。)

特に重要なのは、Libgdxはゲームライブラリです。ゲームは、ほとんどのソフトウェアよりもパフォーマンスに敏感になる傾向があります。また、ゲーム用グラフィックスカード(FireProやQuadroではなく、AMD RadeonやNVIDIA Geforce)は、64ビットの浮動小数点パフォーマンスが非常に弱い傾向があります。Anandtechの厚意により、倍精度のパフォーマンスと、一部のAMDおよびNVIDIAのトップゲーミングカードの単精度のパフォーマンスを比較する方法は次のとおりです(2016年初頭)

AMD
Card    R9 Fury X      R9 Fury       R9 290X    R9 290
FP64    1/16           1/16          1/8        1/8

NVIDIA
Card    GTX Titan X    GTX 980 Ti    GTX 980    GTX 780 Ti
FP64    1/32           1/32          1/32       1/24

R9 FuryおよびGTX 900シリーズはR9 200およびGTX 700シリーズよりも新しいため、64ビット浮動小数点の相対的なパフォーマンスは低下していることに注意してください。十分に遡ると、R9 200シリーズのような1/8の比率のGTX 580が見つかります。

パフォーマンスの1/32は、時間の制約が厳しく、大きなdoubleを使用してもあまり利益が得られない場合に支払う大きなペナルティです。


1
64ビット浮動小数点のパフォーマンスは、実際の64ビットパフォーマンスが低下しているからではなく、高度に最適化された32ビット命令により、32ビットパフォーマンスに比べて低下していることに注意してください。また、使用される実際のベンチマークにも依存します。これらのベンチマークで強調されている32ビットのパフォーマンスの低下は、メモリ帯域幅の問題と実際の計算速度に起因するのではないかと思います
-sig_seg_v

グラフィックカードでのDPのパフォーマンスについて話す場合は、間違いなくTitan / Titan Blackに言及する必要があります。どちらの機能も、単精度のパフォーマンスを犠牲にして、カードが1/3のパフォーマンスに達することを可能にするmodを備えています。
SGR

@sig_seg_v少なくともいくつかのケースでは、64ビットのパフォーマンスが相対的に低下するだけでなく、絶対的に低下する場合があります。GTX 780 TiがGTX 1080(別の1/32比率カード)と980 Tiの両方を打ち、AMD側では7970(1/4比率カード)を上回る倍精度Folding @ Homeベンチマークの結果を参照してください。、R9 290およびR9 290XはすべてR9フューリーシリーズに勝っています。これをベンチマークの単精度バージョンと比較してください。新しいバージョンのカードはすべて、前のバージョンよりも優れたパフォーマンスを発揮します。
8ビットツリー

36

原子操作

他の人がすでに言ったことに加えて、Java固有の欠点double(およびlong)は、64ビットのプリミティブ型への割り当てがアトミックであることが保証されないことです。Java言語仕様のJava SE 8版、ページ660(強調追加):

17.7 doubleおよびの非原子的処理long

Javaプログラミング言語のメモリモデルの目的上、不揮発性longまたはdouble値への単一の書き込みは、32ビットの半分ごとに1つずつの2つの別個の書き込みとして扱われます。これにより、スレッドは1つの書き込みから64ビット値の最初の32ビットを、別の書き込みから2番目の32ビットを見ることができます。

うん

これを回避するには、volatileキーワードを使用して64ビット変数宣言するか、割り当てに関する他の同期形式を使用する必要があります。


2
とにかくintとfloatへの同時アクセスを同期して、更新の損失を防ぎ、過熱キャッシュを防ぐために揮発性にする必要はありませんか?int / floatの原子性が防止する唯一のものは、保持するはずのない「混合」値を決して含むことができないということだと思うのは間違っていますか?
トラウベンフックス

3
@Traubenfuchsつまり、確かにそこで保証されていることです。私が聞いた用語は「引き裂き」であり、その効果を非常にうまく捉えていると思います。Javaプログラミング言語モデルは、32ビット値が読み取られたときに、ある時点で書き込まれた値を持つことを保証します。それは驚くほど貴重な保証です。
コートアンモン

原子性に関するこの点は非常に重要です。うわー、私はこの重要な事実を忘れていました。私たちはプリミティブを本質的にアトミックであると考える傾向があるため、直感に反します。ただし、この場合はアトミックではありません。
バジルブルク

3

他の答えは1つの重要なポイントを見逃しているようです:SIMDアーキテクチャは、操作doubleまたはfloat構造(たとえば、一度に8つのfloat値、または一度に4つのdouble値)に応じてより少ない/より多くのデータを処理できます。

パフォーマンスに関する考慮事項の要約

  • float 特定のCPU(特定のモバイルデバイスなど)では高速になる場合があります。
  • float 使用するメモリが少ないため、巨大なデータセットでは、必要なメモリ(ハードディスク/ RAM)と消費帯域幅の合計を大幅に削減できます。
  • float 倍精度の計算と比較して、単精度の計算の場合、CPUの消費電力が少なくなる可能性があります(参照は見つかりませんが、可能な場合は少なくとももっともらしいようです)。
  • float 消費する帯域幅が少なく、重要なアプリケーションもあります。
  • 通常、SIMDアーキテクチャは同じ量の2倍のデータを処理できます。
  • float ダブルと比較してキャッシュメモリの半分を使用します。

精度に関する考慮事項の概要

  • 多くのアプリケーションfloatで十分です
  • double とにかくはるかに正確です

互換性に関する考慮事項

  • データをGPUに送信する必要がある場合(たとえば、OpenGLまたは他のレンダリングAPI を使用するビデオゲームの場合)、浮動小数点形式はdouble(GPUメーカーがグラフィックコアの数を増やしようとするためです)したがって、各コアで可能な限り多くの回路を節約しようとするため、最適化のためにfloat、より多くのコアを持つGPUを作成できます)
  • 古いGPUと一部のモバイルデバイスdoubleは、内部形式として受け入れられません(3Dレンダリング操作用)

一般的なヒント

  • 最新のデスクトッププロセッサ(おそらくかなりの量のモバイルプロセッサ)では、基本的doubleに、スタックで一時変数を使用すると、精度が無料で得られる(パフォーマンスを犠牲にすることなく余分な精度)と仮定できます。
  • 必要以上の精度を使用しないでください(実際に必要な精度がわからない場合があります)。
  • 値の範囲によって強制される場合があります(を使用している場合は値が無限にfloatなりますが、を使用している場合は値が制限される場合がありますdouble
  • のみ使用するfloatかだけはdouble大幅SIMD-IFYの指示にコンパイラーに役立ちます。

詳細については、以下のPeterCordesのコメントを参照してください。


1
doubletemporariesは、x87 FPUを備えたx86でのみ無料であり、SSE2では無料ではありません。double一時的なループを自動ベクトル化するfloatdouble、にアンパックされ、余分な命令が必要になり、ベクトルごとに半分の要素を処理します。自動ベクトル化を使用しない場合、変換は通常、ロードまたはストア中にオンザフライで発生しますが、式で浮動小数点数と倍精度数を混在させると余分な命令が発生します。
ピーター

1
最新のx86 CPUでは、divとsqrtはdoubleよりもfloatの方が高速ですが、他のことは同じ速度です(SIMDベクトル幅の問題、またはメモリ帯域幅/キャッシュフットプリントはもちろんカウントしません)。
ピーターコーデス

@PeterCordesは、いくつかの点を拡大してくれてありがとう。divとsqrtの不一致を認識していませんでした
-GameDeveloper

0

言及された他の理由とは別に:

圧力、流量、電流、電圧など、測定データがある場合、これはADCを備えたハードウェアでよく行われます。

ADCには通常10または12ビットがあり、14または16ビットはまれです。しかし、16ビットの1つに固執しましょう-フルスケールの周りを測定する場合、1/65535の精度があります。つまり、65534/65535から65535/65535への変更は、このステップ(1/65535)に過ぎません。それはおよそ1.5E-05です。フロートの精度は約1E-07であるため、はるかに優れています。つまりfloat、これらのデータの保存に使用しても何も失われません。

フロートを使用して過度の計算を行うとdoubles、精度の面よりもパフォーマンスがわずかに低下しますが、2 Vまたは2.00002 Vの電圧を測定しただけでは気にしない場合が多いため、多くの場合、その精度は必要ありません。 、この電圧を圧力に変換する場合、3 barまたは3.00003 barのどちらでも構いません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.