Warum sind Floats immer noch Teil der Java-Sprache, wenn stattdessen meistens Doubles empfohlen werden?


84

An jedem Ort, den ich gesehen habe, heißt es, dass doubledas floatin fast jeder Hinsicht überlegen ist . floatwurde doublein Java überholt , warum wird es also immer noch verwendet?

Ich programmiere viel mit Libgdx und sie zwingen Sie zur Verwendung float(DeltaTime usw.), aber es scheint mir, dass doubledie Arbeit mit Libgdx in Bezug auf Speicher und Arbeitsspeicher einfacher ist.

Ich lese auch Wann verwenden Sie float und wann double , aber wenn floates wirklich nur für Zahlen mit vielen Nachkommastellen gut ist, warum können wir dann nicht einfach eine der vielen Variationen von verwenden double?

Gibt es einen Grund, warum die Leute darauf bestehen, Schwimmer zu verwenden, obwohl dies keine wirklichen Vorteile mehr hat? Ist es einfach zu viel Arbeit, um alles zu ändern?



58
Wie um alles in der Welt haben Sie aus den Antworten auf diese Frage gefolgert, dass "Float wirklich nur für Zahlen mit vielen Nachkommastellen gut ist" ?! Sie sagen das direkte Gegenteil !
Ordous

20
@Eames Beachten Sie, wie es heißt "Zahlen", nicht "Ziffern". Floats sind schlechter, wenn Sie Präzision oder Reichweite benötigen, sie sind besser, wenn Sie viele, viele nicht so genaue Daten benötigen. Das sagen diese Antworten.
Ordous

29
Warum haben wir byteund shortund intwann ist es long?
immibis

15
Eine viel zutreffendere Frage lautet: "Warum sollten Sie ein Schlüsselwort und einen primitiven Datentyp aus einer Sprache mit jahrzehntelangem Code entfernen, die sich ohne Grund auflösen würden?"
Sara

Antworten:


169

LibGDX ist ein Framework, das hauptsächlich für die Spieleentwicklung verwendet wird.

In der Spieleentwicklung muss man normalerweise eine Menge Zahlen in Echtzeit schreiben und jede Leistung, die man bekommen kann, ist von Bedeutung. Das ist der Grund, warum Spieleentwickler normalerweise float verwenden, wenn die float-Präzision ausreicht.

Die Größe der FPU-Register in der CPU ist nicht das einzige, was Sie in diesem Fall berücksichtigen müssen. Tatsächlich wird der Großteil der Probleme bei der Entwicklung von Spielen von der GPU verursacht, und die GPUs sind in der Regel für Floats optimiert, nicht für Double .

Und dann gibt es noch:

  • Speicherbusbandbreite (wie schnell Sie Daten zwischen RAM, CPU und GPU übertragen können)
  • CPU-Cache (was den vorherigen weniger notwendig macht)
  • RAM
  • VRAM

Dies sind alles wertvolle Ressourcen, von denen Sie doppelt so viel erhalten, wenn Sie 32-Bit-Float anstelle von 64-Bit-Double verwenden.


2
Danke! Dies hat wirklich geholfen, da Sie ausführlich beschrieben haben, was sich an der Speichernutzung geändert hat und warum
Eames

7
Bei SIMD-Vorgängen können 32-Bit-Werte den doppelten Durchsatz haben. Wie die Antwort von 8bittree zeigt, haben GPUs einen noch größeren Leistungsverlust mit doppelter Genauigkeit.
Paul A. Clayton

5
Viele Grafik-Pipelines unterstützen sogar 16-Bit-Half-Floats, um die Leistung bei ausreichender Präzision zu steigern.
Adi Shavit

22
@phresnel Alle sind. Sie müssen Positionen verschieben, Daten aktualisieren und was nicht. Und das ist der einfache Teil. Dann müssen Sie die Texturen und Abstände rendern (= lesen, drehen, skalieren und übersetzen) und in das Bildschirmformat bringen ... Es gibt viel zu tun.
Sebb

8
@phresnel Als ehemaliger Vice President Operations eines Spieleentwicklungsunternehmens versichere ich Ihnen, dass in fast jedem Spiel eine Menge Zahlen knacken. Beachten Sie, dass es normalerweise in Bibliotheken enthalten ist und zu 100% vom Ingenieur abstrahiert ist. Ich hoffe, dass sie verstehen und respektieren, dass all dieses Knirschen stattfindet. Magische Quadratwurzel, jemand?
CorsiKa

57

Floats belegen halb so viel Speicher wie Doubles.

Sie haben möglicherweise eine geringere Präzision als Doppelte, aber viele Anwendungen erfordern keine Präzision. Sie haben eine größere Reichweite als alle ähnlich großen Festkommaformate. Daher füllen sie eine Nische, die große Zahlenbereiche erfordert, aber keine hohe Präzision erfordert und bei der die Speichernutzung wichtig ist. Ich habe sie in der Vergangenheit zum Beispiel für große neuronale Netzwerksysteme verwendet.

Außerhalb von Java werden sie auch häufig in 3D-Grafiken verwendet, da sie von vielen GPUs als primäres Format verwendet werden. Außerhalb von sehr teuren NVIDIA Tesla / AMD FirePro-Geräten ist die Gleitkommazahl mit doppelter Genauigkeit auf GPUs sehr langsam.


8
Bei neuronalen Netzen unterstützt CUDA derzeit Gleitkommavariablen mit halber Genauigkeit (16-Bit), die aufgrund der zunehmenden Verwendung von Beschleunigern für maschinelles Lernen noch ungenauer sind, aber einen noch geringeren Speicherbedarf aufweisen.
JAB

Und wenn Sie FPGAs programmieren, neigen Sie dazu, die Anzahl der Bits für Mantisse und Exponent jedes Mal manuell auszuwählen: v
Sebi

48

Rückwärtskompatibilität

Dies ist der Hauptgrund für das Beibehalten des Verhaltens in einer bereits vorhandenen Sprache / Bibliothek / ISA / etc.

Überlegen Sie, was passieren würde, wenn sie Java-Floats entziehen. Libgdx (und Tausende anderer Bibliotheken und Programme) würden nicht funktionieren. Es wird eine Menge Mühe kosten, um alles auf den neuesten Stand zu bringen, möglicherweise Jahre für viele Projekte (schauen Sie sich nur den Übergang von Python 2 zu Python 3 an, der die Abwärtskompatibilität beeinträchtigt). Und nicht alles wird aktualisiert, manche Dinge werden für immer kaputt sein, weil die Betreuer sie aufgegeben haben, vielleicht früher als sie es getan hätten, weil es mehr Mühe kosten würde, als sie aktualisieren möchten, oder weil es nicht mehr möglich ist, das zu erreichen, was ihre Software angeblich war machen.

Performance

64-Bit-Doubles nehmen das Doppelte des Speichers in Anspruch und sind fast immer langsamer als 32-Bit-Floats (mit den seltensten Ausnahmen, bei denen die 32-Bit-Float-Fähigkeit so selten oder gar nicht verwendet wird, dass keine Optimierungsanstrengungen unternommen wurden) Solange Sie nicht für spezielle Hardware entwickeln, werden Sie dies in naher Zukunft nicht erleben.)

Libgdx ist eine Spielebibliothek, die für Sie besonders relevant ist. Spiele sind tendenziell leistungsempfindlicher als die meisten anderen Programme. Und Gaming-Grafikkarten (z. B. AMD Radeon und NVIDIA Geforce, nicht FirePro oder Quadro) weisen in der Regel eine sehr schwache 64-Bit-Gleitkomma-Leistung auf. Mit freundlicher Genehmigung von Anandtech finden Sie hier, wie die Leistung mit doppelter Präzision mit der Leistung mit einfacher Präzision bei einigen der besten verfügbaren Spielekarten von AMD und NVIDIA verglichen wird (ab Anfang 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

Beachten Sie, dass die Serien R9 Fury und GTX 900 neuer sind als die Serien R9 200 und GTX 700, sodass die relative Leistung für 64-Bit-Gleitkommazahlen abnimmt. Gehen Sie weit genug zurück und Sie werden die GTX 580 finden, die ein 1/8 Verhältnis wie die R9 200 Serie hatte.

1/32 der Leistung ist eine ziemlich große Strafe, die zu zahlen ist, wenn Sie einen engen Zeitrahmen haben und mit dem größeren Doppel nicht viel gewinnen.


1
Beachten Sie, dass die Leistung für 64-Bit-Gleitkommazahlen im Vergleich zur 32-Bit-Leistung aufgrund zunehmend optimierter 32-Bit-Befehle abnimmt, nicht weil die tatsächliche 64-Bit-Leistung abnimmt. Dies hängt auch vom tatsächlich verwendeten Benchmark ab. Ich frage mich, ob das in diesen Benchmarks hervorgehobene 32-Bit-Leistungsdefizit auf Probleme mit der Speicherbandbreite sowie auf die tatsächliche Rechengeschwindigkeit zurückzuführen ist
sig_seg_v vom

Wenn Sie über die DP-Leistung von Grafikkarten sprechen möchten, sollten Sie auf jeden Fall den Titan / Titan Black erwähnen. Beide Mods ermöglichen es der Karte, eine Leistung von 1/3 zu erreichen, auf Kosten der Leistung mit einfacher Präzision.
SGR

@sig_seg_v Es gibt definitiv zumindest einige Fälle, in denen die 64-Bit-Leistung absolut abnimmt, nicht nur relativ. Sehen Sie sich diese Ergebnisse für einen doppelt präzisen Folding @ Home-Benchmark an, bei dem eine GTX 780 Ti sowohl eine GTX 1080 (eine Karte mit einem Verhältnis von 1/32) als auch eine 980 Ti und auf der AMD-Seite die 7970 (eine Karte mit einem Verhältnis von 1/4) schlägt. , sowie der R9 290 und R9 290X schlagen alle die R9 Fury-Serie. Vergleichen Sie das mit der Single-Precision-Version des Benchmarks , bei der die neueren Karten alle ihre Vorgänger deutlich übertreffen.
8bittree

36

Atomare Operationen

Zusätzlich zu dem, was andere bereits gesagt haben, besteht ein Java-spezifischer Nachteil von double(und long) darin, dass Zuweisungen zu primitiven 64-Bit-Typen nicht garantiert atomar sind . Aus der Java-Sprachspezifikation, Java SE 8 Edition , Seite 660 (Hervorhebung hinzugefügt):

17.7 Nichtatomare Behandlung von doubleundlong

Für die Zwecke des Speichermodells der Programmiersprache Java wird ein einzelnes Schreiben in einen nichtflüchtigen Wert longoder doubleWert als zwei separate Schreibvorgänge behandelt: einer in jede 32-Bit-Hälfte. Dies kann zu einer Situation führen, in der ein Thread die ersten 32 Bits eines 64-Bit-Werts von einem Schreibvorgang und die zweiten 32 Bits von einem anderen Schreibvorgang sieht.

Yuck.

Um dies zu vermeiden, müssen Sie die 64-Bit-Variable mit demvolatile Schlüsselwort deklarieren oder eine andere Form der Synchronisierung für Zuweisungen verwenden.


2
Müssen Sie nicht den gleichzeitigen Zugriff auf Ints und Floats synchronisieren, um verlorene Updates zu verhindern und sie flüchtig zu machen, um übermäßiges Caching zu verhindern? Liege ich falsch, wenn ich denke, dass die Atomarität von int / float nur verhindert, dass sie niemals "gemischte" Werte enthalten können, die sie eigentlich nicht haben sollten?
Traubenfuchs

3
@Traubenfuchs Das ist ja was da garantiert ist. Der Begriff, den ich dafür gehört habe, ist "Zerreißen", und ich denke, er fängt den Effekt ziemlich gut ein. Das Java-Programmiersprachenmodell garantiert, dass 32-Bit-Werte beim Lesen einen Wert haben, der zu einem bestimmten Zeitpunkt darauf geschrieben wurde. Das ist eine überraschend wertvolle Garantie.
Cort Ammon

Dieser Punkt über die Atomizität ist überaus wichtig. Wow, ich hatte diese wichtige Tatsache vergessen. Kontraintuitiv, wie wir uns Primitive von Natur aus als atomar vorstellen können. Aber in diesem Fall nicht atomar.
Basil Bourque

3

Andere Antworten scheinen einen wichtigen Punkt übersehen zu haben: Die SIMD- Architekturen können weniger / mehr Daten verarbeiten, je nachdem, ob sie mit doubleoder ohne floatStrukturen arbeiten (z. B. acht Gleitkommawerte gleichzeitig oder vier Doppelwerte gleichzeitig).

Zusammenfassung der Leistungsaspekte

  • float kann auf bestimmten CPUs (z. B. bestimmten Mobilgeräten) schneller sein.
  • float Benötigt weniger Speicher, so dass in großen Datenmengen der insgesamt erforderliche Speicher (Festplatte / RAM) und die verbrauchte Bandbreite erheblich reduziert werden können.
  • float Dies kann dazu führen, dass eine CPU weniger Strom verbraucht (ich kann keine Referenz finden, aber wenn dies nicht möglich ist, scheint dies zumindest plausibel zu sein).
  • float verbraucht weniger Bandbreite und ist in einigen Anwendungen von Bedeutung.
  • SIMD-Architekturen können in der Regel doppelt so viele Daten verarbeiten.
  • float Verwendet bis zu die Hälfte des Cache-Speichers im Vergleich zu doppelt.

Zusammenfassung der Überlegungen zur Genauigkeit

  • In vielen Anwendungen floatreicht das aus
  • double hat sowieso viel mehr präzision

Überlegungen zur Kompatibilität

  • Wenn Ihre Daten an eine GPU gesendet werden müssen (z. B. für ein Videospiel mit OpenGL oder einer anderen Rendering-API), ist das Gleitkommaformat erheblich schneller als double(weil die GPU-Hersteller versuchen, die Anzahl der Grafikkerne zu erhöhen, und) Aus diesem Grund wird versucht, so viel Schaltkreise wie möglich in jedem Kern zu sparen. Durch die Optimierung für floatkönnen GPUs mit mehr Kernen erstellt werden.
  • Alte GPUs und einige Mobilgeräte können nicht doubleals internes Format akzeptiert werden (für 3D-Rendering-Vorgänge)

Allgemeine Hinweise

  • Auf modernen Desktop-Prozessoren (und wahrscheinlich einer großen Anzahl mobiler Prozessoren) können Sie im Grunde davon ausgehen, dass die Verwendung temporärer doubleVariablen auf dem Stack eine zusätzliche Präzision bietet (zusätzliche Präzision ohne Leistungseinbußen).
  • Verwenden Sie niemals mehr Präzision als Sie benötigen (Sie wissen möglicherweise nicht, wie viel Präzision Sie wirklich benötigen).
  • Manchmal werden Sie nur durch den Wertebereich gezwungen (einige Werte sind bei Verwendung unendlich, bei Verwendung floatjedoch möglicherweise nur begrenzte Werte double).
  • Nur floatoder nur doublesehr hilfreich für den Compiler, um die Anweisungen zu simulieren.

Weitere Informationen finden Sie in den Kommentaren von PeterCordes.


1
doubletemporaries ist nur auf x86 mit der x87-FPU kostenlos, nicht mit SSE2. Das automatische Vektorisieren einer Schleife mit doubletemporären Elementen bedeutet das Entpacken floatin double, für das eine zusätzliche Anweisung erforderlich ist, und Sie verarbeiten die Hälfte der Elemente pro Vektor. Ohne automatische Vektorisierung kann die Konvertierung normalerweise spontan während eines Ladens oder Speicherns erfolgen. Dies bedeutet jedoch zusätzliche Anweisungen, wenn Sie Floats und Doubles in Ausdrücken mischen.
Peter Cordes

1
Auf modernen x86-CPUs sind div und sqrt schneller für float als double, aber andere Dinge sind gleich schnell (ohne Berücksichtigung des SIMD-Vektorbreitenproblems oder der Speicherbandbreite / des Cache-Platzbedarfs natürlich).
Peter Cordes

@PeterCordes danke für das Erweitern einiger Punkte. Ich war mir der Unterschiede
zwischen

0

Abgesehen von den anderen genannten Gründen:

Wenn Sie Messdaten haben, seien es Drücke, Flüsse, Ströme, Spannungen oder was auch immer, wird dies häufig mit Hardware mit einem ADC durchgeführt.

Ein ADC hat normalerweise 10 oder 12 Bits, 14 oder 16 Bits sind seltener. Aber bleiben wir bei der 16-Bit-Version - wenn Sie im Vollbereich messen, haben Sie eine Genauigkeit von 1/65535. Das bedeutet, dass ein Wechsel von 65534/65535 zu 65535/65535 genau dieser Schritt ist - 1/65535. Das ist ungefähr 1.5E-05. Die Genauigkeit eines Schwimmers liegt um 1E-07, also viel besser. Sie verlieren also nichts, wenn Sie floatdiese Daten speichern.

Wenn Sie übermäßige Berechnungen mit Schwebekörpern durchführen, ist die doublesGenauigkeit geringfügig schlechter als die von Schwebekörpern. Diese Genauigkeit wird jedoch häufig nicht benötigt, da es Ihnen oft egal ist, ob Sie nur eine Spannung von 2 V oder 2,00002 V gemessen haben Wenn Sie diese Spannung in einen Druck umwandeln, ist es Ihnen egal, ob Sie 3 bar oder 3,00003 bar haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.