Warum hat C keine vorzeichenlosen Floats?

Lesezeit: 2 Minuten

Benutzeravatar von Nils Pipenbrinck
Nils Pipenbrink

Ich weiß, die Frage scheint seltsam zu sein. Programmierer denken manchmal zu viel nach. Bitte weiterlesen…

Im CI-Einsatz signed und unsigned ganze Zahlen. Mir gefällt die Tatsache, dass der Compiler mich warnt, wenn ich beispielsweise einer vorzeichenlosen Variablen eine vorzeichenbehaftete Ganzzahl zuweise. Ich erhalte Warnungen, wenn ich vorzeichenbehaftete mit vorzeichenlosen Ganzzahlen vergleiche und vieles mehr.

Ich mag diese Warnungen. Sie helfen mir, meinen Code korrekt zu halten.

Warum haben wir nicht den gleichen Luxus für Festwagen? Eine Quadratwurzel wird definitiv niemals eine negative Zahl zurückgeben. Es gibt auch andere Stellen, an denen ein negativer Float-Wert keine Bedeutung hat. Perfekter Kandidat für einen unsignierten Schwimmer.

Übrigens – ich bin nicht wirklich scharf auf das einzelne zusätzliche Bit an Präzision, das ich durch Entfernen des Vorzeichenbits aus den Floats erhalten könnte. Ich bin super zufrieden mit floats, wie sie gerade sind. Ich möchte nur Markieren Sie einen Float als unsigned manchmal und bekomme die gleiche Art von Warnungen, die ich mit ganzen Zahlen bekomme.

Mir ist keine Programmiersprache bekannt, die vorzeichenlose Gleitkommazahlen unterstützt.

Irgendeine Idee, warum es sie nicht gibt?


BEARBEITEN:

Ich weiß, dass die x87-FPU keine Anweisungen zum Umgang mit vorzeichenlosen Floats hat. Verwenden wir einfach die signierten Float-Anweisungen. Missbrauch (z. B. Unterschreiten von Null) könnte als undefiniertes Verhalten betrachtet werden, genauso wie ein Überlauf von vorzeichenbehafteten Ganzzahlen undefiniert ist.

  • Interessant, können Sie ein Beispiel für einen Fall posten, in dem die Typprüfung der Signatur hilfreich war?

    Iraimbilanja

    4. Februar 2009 um 16:10 Uhr

  • litb, war Ihr Kommentar an mich gerichtet? wenn ja, verstehe ich es nicht

    Iraimbilanja

    4. Februar 2009 um 16:37 Uhr

  • Iraimbilanja yeah 🙂 fabs kann keine negative Zahl zurückgeben, da es den absoluten Wert seines Arguments zurückgibt

    – Johannes Schaub – litb

    4. Februar 2009 um 16:42 Uhr

  • Richtig. Ich habe nicht gefragt, wie ein hypothetischer unsignedfloat zur Korrektheit beitragen könnte. Was ich gefragt habe, war: In welcher Situation fand Pipenbrinck die Int-Signatur-Typprüfung hilfreich (was ihn dazu veranlasste, denselben Mechanismus für Floats zu suchen). Der Grund, den ich frage, ist, dass ich unsigneds völlig nutzlos finde in Bezug auf Typsicherheit

    Iraimbilanja

    4. Februar 2009 um 19:16 Uhr

  • Es gibt eine unsignierte Mikrooptimierung für die Point-in-Range-Prüfung: ((unsigned)(p-min))<(max-min), die nur einen Zweig hat, aber wie immer ist es am besten, ein Profil zu erstellen, um zu sehen, ob es hilft wirklich (ich habe es meistens auf 386 Kernen verwendet, daher weiß ich nicht, wie moderne CPUs damit umgehen).

    – Schizz

    5. Februar 2009 um 9:21 Uhr

Benutzeravatar von Brian R. Bondy
Brian R. Bondy

Der Grund, warum C++ keine vorzeichenlosen Gleitkommazahlen unterstützt, liegt daran, dass es keine entsprechenden Maschinencodeoperationen gibt, die die CPU ausführen könnte. Es wäre also sehr ineffizient, es zu unterstützen.

Wenn C++ dies unterstützen würde, würden Sie manchmal ein vorzeichenloses Gleitkomma verwenden und nicht erkennen, dass Ihre Leistung gerade beeinträchtigt wurde. Wenn C++ dies unterstützen würde, müsste jede Gleitkommaoperation überprüft werden, um festzustellen, ob sie signiert ist oder nicht. Und für Programme, die Millionen von Gleitkommaoperationen ausführen, ist dies nicht akzeptabel.

Die Frage wäre also, warum Hardware-Implementierer dies nicht unterstützen. Und ich denke, die Antwort darauf ist, dass ursprünglich kein vorzeichenloser Float-Standard definiert wurde. Da Sprachen gerne abwärtskompatibel sind, könnten Sprachen, selbst wenn sie hinzugefügt würden, keinen Gebrauch davon machen. Um die Fließkommaspezifikation zu sehen, sollten Sie sich die anschauen IEEE-Standard 754 Fließkomma.

Sie können jedoch umgehen, keinen vorzeichenlosen Gleitkommatyp zu haben, indem Sie eine vorzeichenlose Float-Klasse erstellen, die ein Float oder Double kapselt und Warnungen ausgibt, wenn Sie versuchen, eine negative Zahl zu übergeben. Dies ist weniger effizient, aber wenn Sie sie nicht intensiv verwenden, wird Ihnen dieser leichte Leistungsverlust wahrscheinlich egal sein.

Ich sehe definitiv den Nutzen eines unsignierten Floats. Aber C/C++ tendiert dazu, die Effizienz, die für alle am besten funktioniert, der Sicherheit vorzuziehen.

  • C/C++ erfordert keine speziellen Maschinencodeoperationen, um die Sprache zu implementieren. Frühe C/C++-Compiler konnten Fließkommacode für den 386 generieren – eine CPU ohne FPU! Der Compiler würde Bibliotheksaufrufe generieren, um FPU-Anweisungen zu emulieren. Daher könnte ein Ufloat ohne CPU-Unterstützung durchgeführt werden

    – Schizz

    5. Februar 2009 um 9:44 Uhr

  • Skizz, obwohl das richtig ist, hat Brian dies bereits angesprochen – dass die Leistung im Vergleich dazu schrecklich sein wird, weil es keinen entsprechenden Maschinencode gibt.

    – Antonius

    29. April 2009 um 18:33 Uhr

  • @ Brian R. Bondy: Ich habe dich hier verloren: “weil es keine äquivalenten Maschinencodeoperationen gibt, die die CPU ausführen kann …”. Können Sie das bitte in einfacheren Worten erklären?

    – Laser

    17. April 2010 um 19:39 Uhr

  • Der Grund, warum OP Unterstützung für vorzeichenlose Floats wollte, waren Warnmeldungen, also hat es wirklich nichts mit der Codegenerierungsphase des Compilers zu tun – nur damit, wie er die Typprüfung im Voraus durchführt -, daher ist die Unterstützung für sie im Maschinencode irrelevant und (wie am Ende der Frage hinzugefügt wurde) normale Gleitkommaanweisungen könnten für die tatsächliche Ausführung verwendet werden.

    – Jo F

    12. April 2013 um 10:52 Uhr


  • Ich bin mir nicht sicher, ob ich verstehe, warum dies die Leistung beeinträchtigen sollte. Genauso wie mit int‘s könnte die gesamte zeichenbezogene Typprüfung zur Kompilierzeit erfolgen. OP schlägt das vor unsigned float würde regulär umgesetzt werden float mit Überprüfungen zur Kompilierzeit, um sicherzustellen, dass bestimmte nicht sinnvolle Operationen niemals ausgeführt werden. Der resultierende Maschinencode und die Leistung könnten identisch sein, unabhängig davon, ob Ihre Gleitkommazahlen signiert sind oder nicht.

    – Xanderflut

    12. April 2017 um 18:02 Uhr

Benutzeravatar von Skizz
Schizz

Es gibt einen signifikanten Unterschied zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlen in C/C++:

value >> shift

Vorzeichenbehaftete Werte lassen das oberste Bit unverändert (Sign Extend), vorzeichenlose Werte löschen das oberste Bit.

Der Grund, warum es keinen vorzeichenlosen Float gibt, ist, dass Sie schnell auf alle möglichen Probleme stoßen, wenn keine negativen Werte vorhanden sind. Bedenken Sie:

float a = 2.0f, b = 10.0f, c;
c = a - b;

Welchen Wert hat c? -8. Aber was würde das in einem System ohne negative Zahlen bedeuten. FLOAT_MAX – 8 vielleicht? Eigentlich funktioniert das nicht, da FLOAT_MAX – 8 aufgrund von Präzisionseffekten FLOAT_MAX ist, also sind die Dinge noch verrückter. Was wäre, wenn es Teil eines komplexeren Ausdrucks wäre:

float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
e = (a - b) / d + c;

Dies ist aufgrund der Natur des 2er-Komplementsystems kein Problem für ganze Zahlen.

Berücksichtigen Sie auch mathematische Standardfunktionen: sin, cos und tan würden nur für die Hälfte ihrer Eingabewerte funktionieren, Sie könnten den Logarithmus von Werten < 1 nicht finden, Sie könnten keine quadratischen Gleichungen lösen: x = (-b +/- root ( bb - 4.ac)) / 2.a und so weiter. Tatsächlich würde es wahrscheinlich nicht für komplexe Funktionen funktionieren, da diese dazu neigen, als polynomische Annäherungen implementiert zu werden, die irgendwo negative Werte verwenden würden.

Unsignierte Floats sind also ziemlich nutzlos.

Aber das bedeutet nicht, dass eine Klasse, die Gleitkommawerte überprüft, nicht nützlich ist, Sie möchten vielleicht Werte auf einen bestimmten Bereich beschränken, z. B. RGB-Berechnungen.

  • @Skizz: Wenn die Darstellung ein Problem ist, meinen Sie, wenn jemand eine Methode zum Speichern von Gleitkommazahlen entwickeln kann, die so effizient ist wie 2's complementgibt es kein Problem mit unsignierten Floats?

    – Laser

    17. April 2010 um 19:57 Uhr

  • value >> shift for signed values leave the top bit unchanged (sign extend) Bist du dir da sicher? Ich dachte, das sei implementierungsdefiniertes Verhalten, zumindest für negative vorzeichenbehaftete Werte.

    – Dan

    5. Februar 2012 um 15:55 Uhr

  • @Dan: Ich habe mir gerade den aktuellen Standard angesehen und er besagt tatsächlich, dass er implementierungsdefiniert ist – ich denke, das ist nur für den Fall, dass es eine CPU gibt, die keine Rechtsverschiebung mit Vorzeichenerweiterungsbefehl hat.

    – Schizz

    5. Februar 2012 um 19:07 Uhr

  • Verschiebung nach rechts mit Vorzeichen: Welcher Compiler verwendet die logische Verschiebung

    – phuklv

    19. Februar 2019 um 14:02 Uhr

  • Gleitkommazahlen werden traditionell gesättigt (zu -/+Inf), anstatt umgebrochen zu werden. Sie könnten erwarten, dass der Überlauf der vorzeichenlosen Subtraktion zu sättigen ist 0.0, oder möglicherweise Inf oder NaN. Oder seien Sie einfach undefiniertes Verhalten, wie das OP in einer Bearbeitung der Frage vorgeschlagen hat. Betreff: Triggerfunktionen: Definieren Sie also keine Versionen von ohne Vorzeichen sin und so weiter, und stellen Sie sicher, dass ihr Rückgabewert als signiert behandelt wird. Die Frage war nicht vorschlagen ersetzen Float mit vorzeichenlosem Float, nur hinzufügen unsigned float als neue Art.

    – Peter Cordes

    30. März 2020 um 9:30 Uhr


Benutzeravatar von ephemient
vergänglich

(Nebenbei gesagt, Perl 6 lässt Sie schreiben

subset Nonnegative::Float of Float where { $_ >= 0 };

und dann können Sie verwenden Nonnegative::Float genau wie jeder andere Typ.)

Es gibt keine Hardwareunterstützung für vorzeichenlose Gleitkommaoperationen, also bietet C sie nicht an. C ist hauptsächlich als “tragbare Montage” konzipiert, dh so nah am Metall wie möglich, ohne an eine bestimmte Plattform gebunden zu sein.

[edit]

C ist wie Montage: Was Sie sehen, ist genau das, was Sie bekommen. Ein implizites “Ich werde überprüfen, ob dieser Schwimmer für Sie nicht negativ ist” widerspricht seiner Designphilosophie. Wenn Sie es wirklich wollen, können Sie hinzufügen assert(x >= 0) oder ähnliches, aber das muss man explizit machen.

Benutzeravatar von Treb
Treb

Ich glaube, dass das unsigned int erstellt wurde, weil eine größere Wertspanne erforderlich ist, als das signed int bieten könnte.

Ein Float hat einen viel größeren Spielraum, daher gab es nie einen „physischen“ Bedarf für einen vorzeichenlosen Float. Und wie Sie in Ihrer Frage selbst betonen, ist die zusätzliche 1-Bit-Präzision nichts, wofür man töten könnte.

Bearbeiten:
Nachdem ich die Antwort von Brian R. Bondy gelesen habe, muss ich meine Antwort ändern: Er hat definitiv Recht, dass die zugrunde liegenden CPUs keine vorzeichenlosen Float-Operationen hatten. Ich bleibe jedoch bei meiner Überzeugung, dass dies eine Designentscheidung war, die auf den oben genannten Gründen basiert 😉

Ich denke, Treb ist auf dem richtigen Weg. Bei Ganzzahlen ist es wichtiger, dass Sie einen entsprechenden Typ ohne Vorzeichen haben. Das sind die, die verwendet werden Bitverschiebung und verwendet in Bitmaps. Ein Zeichenbit kommt nur in die Quere. Wenn Sie beispielsweise einen negativen Wert nach rechts verschieben, ist der resultierende Wert die in C++ definierte Implementierung. Wenn Sie dies mit einer vorzeichenlosen Ganzzahl tun oder eine solche überlaufen lassen, hat dies eine perfekt definierte Semantik, da kein solches Bit im Weg ist.

Zumindest für Ganzzahlen ist die Notwendigkeit eines separaten Typs ohne Vorzeichen stärker als nur das Ausgeben von Warnungen. Alle oben genannten Punkte müssen bei Schwimmern nicht berücksichtigt werden. Ich denke also, es besteht kein wirklicher Bedarf an Hardwareunterstützung für sie, und C wird sie zu diesem Zeitpunkt bereits nicht unterstützen.

Eine Quadratwurzel wird definitiv niemals eine negative Zahl zurückgeben. Es gibt auch andere Stellen, an denen ein negativer Float-Wert keine Bedeutung hat. Perfekter Kandidat für einen unsignierten Schwimmer.

C99 unterstützt komplexe Zahlen und eine typgenerische Form von sqrt, also sqrt( 1.0 * I) wird negativ sein.


Die Kommentatoren haben oben eine leichte Anmerkung hervorgehoben, indem ich mich auf das Typ-Generikum bezog sqrt Makro und nicht die Funktion, und es gibt einen skalaren Gleitkommawert zurück, indem der Komplex auf seine reale Komponente gekürzt wird:

#include <complex.h>
#include <tgmath.h>

int main () 
{
    complex double a = 1.0 + 1.0 * I;

    double f = sqrt(a);

    return 0;
}

Es enthält auch einen Hirnfurz, da der Realteil der Quadratzahl jeder komplexen Zahl positiv oder null ist und sqrt(1,0*I) sqrt(0,5) + sqrt(0,5)*I ist und nicht -1,0.

Benutzeravatar von phuclv
phuclv

Ich denke, es hängt davon ab, dass die IEEE-Gleitkommaspezifikationen nur signiert sind und dass die meisten Programmiersprachen sie verwenden.

Wikipedia-Artikel über IEEE-754-Gleitkommazahlen

Bearbeiten: Außerdem unterstützt die meiste Hardware, wie von anderen angemerkt, keine nicht negativen Gleitkommazahlen, sodass die normale Art von Gleitkommazahlen effizienter ist, da Hardwareunterstützung vorhanden ist.

  • C wurde eingeführt, lange bevor der IEEE-754-Standard erschien

    – phuklv

    19. Februar 2019 um 14:04 Uhr

  • @phuclv Beides war keine übliche Gleitkomma-Hardware. Es wurde “ein paar” Jahre später in den Standard C übernommen. Vermutlich kursieren im Internet Dokumentationen dazu. (Auch der Wikipedia-Artikel erwähnt C99).

    – Tobias Wärre

    12. März 2019 um 12:26 Uhr

  • Ich verstehe nicht, was du meinst. Ihre Antwort enthält keine “Hardware”, und IEEE-754 wurde nach C geboren, sodass Gleitkommatypen in C nicht vom IEEE-754-Standard abhängen können, es sei denn, diese Typen wurden viel später in C eingeführt

    – phuklv

    12. März 2019 um 12:55 Uhr

  • @phuclv C ist/war auch als portable Assembly bekannt, daher kann es der Hardware ziemlich nahe kommen. Sprachen gewinnen im Laufe der Jahre an Funktionen, auch wenn Float (vor meiner Zeit) in C implementiert wurde, war es wahrscheinlich eine softwarebasierte Operation und ziemlich teuer. Zum Zeitpunkt der Beantwortung dieser Frage hatte ich offensichtlich ein besseres Verständnis dafür, was ich zu erklären versuchte, als ich es jetzt tue. Und wenn Sie sich die akzeptierte Antwort ansehen, verstehen Sie vielleicht, warum ich den IEE754-Standard erwähnt habe. Was ich nicht verstehe, ist, dass Sie auf einer 10 Jahre alten Antwort herumgepfuscht haben, die nicht die akzeptierte ist?

    – Tobias Wärre

    25. März 2019 um 15:31 Uhr

1424140cookie-checkWarum hat C keine vorzeichenlosen Floats?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy