Ich weiß, die Frage scheint seltsam zu sein. Programmierer denken manchmal zu viel. Bitte lesen Sie weiter ...
In CI verwenden signed
und unsigned
ganze Zahlen viel. Ich mag die Tatsache, dass der Compiler mich warnt, wenn ich 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 Schwimmer? 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 nicht signierten Wagen.
Übrigens - ich bin nicht wirklich begeistert von der zusätzlichen Präzision, die ich durch Entfernen des Vorzeichenbits von den Schwimmern erzielen könnte. Ich bin super glücklich mit float
s, wie sie gerade sind. Ich möchte einen Float nur manchmal als nicht signiert markieren und die gleichen Warnungen erhalten, die ich mit ganzen Zahlen erhalte.
Mir ist keine Programmiersprache bekannt, die vorzeichenlose Gleitkommazahlen unterstützt.
Irgendeine Idee, warum sie nicht existieren?
BEARBEITEN:
Ich weiß, dass die x87-FPU keine Anweisungen zum Umgang mit nicht signierten Floats hat. Verwenden wir einfach die signierten Float-Anweisungen. Missbrauch (z. B. unter Null) kann als undefiniertes Verhalten angesehen werden, genauso wie der Überlauf von vorzeichenbehafteten Ganzzahlen undefiniert ist.
Antworten:
Warum C ++ keine vorzeichenlosen Floats unterstützt, liegt daran, dass die CPU keine entsprechenden Maschinencodeoperationen ausführen kann. Es wäre also sehr ineffizient, dies zu unterstützen.
Wenn C ++ dies unterstützt, verwenden Sie manchmal einen nicht signierten Float und bemerken nicht, dass Ihre Leistung gerade beeinträchtigt wurde. Wenn C ++ dies unterstützt, muss 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önnen Sprachen sie auch dann nicht nutzen, wenn sie hinzugefügt wurden. Um die Gleitkomma-Spezifikation zu sehen, sollten Sie sich den Gleitkomma-Standard nach IEEE-Standard 754 ansehen .
Sie können jedoch umgehen, dass Sie keinen vorzeichenlosen Gleitkommatyp haben, indem Sie eine vorzeichenlose Gleitkommaklasse erstellen, die einen Gleitkomma- oder Doppeltyp kapselt und Warnungen auslöst, wenn Sie versuchen, eine negative Zahl zu übergeben. Dies ist weniger effizient, aber wenn Sie sie nicht intensiv nutzen, ist Ihnen dieser leichte Leistungsverlust wahrscheinlich egal.
Ich sehe definitiv die Nützlichkeit eines nicht signierten Schwimmers. C / C ++ tendiert jedoch dazu, eine Effizienz zu wählen, die für alle am besten geeignet ist, wenn es um Sicherheit geht.
quelle
int
's kann die gesamte zeichenbezogene Typprüfung zur Kompilierungszeit erfolgen. OP schlägt vor,unsigned float
dies regelmäßigfloat
mit Überprüfungen zur Kompilierungszeit zu implementieren , um sicherzustellen, dass bestimmte nicht aussagekräftige Vorgänge niemals ausgeführt werden. Der resultierende Maschinencode und die Leistung können identisch sein, unabhängig davon, ob Ihre Floats signiert sind oder nicht.In C / C ++ gibt es einen signifikanten Unterschied zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlen:
Vorzeichenbehaftete Werte lassen das oberste Bit unverändert (Vorzeichenverlängerung), vorzeichenlose Werte löschen das oberste Bit.
Der Grund dafür, dass es kein vorzeichenloses Float gibt, ist, dass Sie schnell auf alle möglichen Probleme stoßen, wenn keine negativen Werte vorhanden sind. Bedenken Sie:
Welchen Wert hat c? -8. Aber was würde das in einem System ohne negative Zahlen bedeuten? FLOAT_MAX - 8 vielleicht? Tatsächlich funktioniert das nicht, da FLOAT_MAX - 8 aufgrund von Präzisionseffekten FLOAT_MAX ist, sodass die Dinge noch schwieriger werden. Was wäre, wenn es Teil eines komplexeren Ausdrucks wäre:
Dies ist aufgrund der Art des 2-Komplementsystems kein Problem für ganze Zahlen.
Berücksichtigen Sie auch mathematische Standardfunktionen: sin, cos und tan funktionieren nur für die Hälfte ihrer Eingabewerte, Sie konnten das Protokoll der Werte <1 nicht finden, Sie konnten quadratische Gleichungen nicht lösen: x = (-b +/- root ( bb - 4.ac)) / 2.a und so weiter. Tatsächlich würde es wahrscheinlich für keine komplexe Funktion funktionieren, da diese tendenziell als Polynomnäherungen implementiert werden, die irgendwo negative Werte verwenden würden.
Nicht signierte Schwimmer sind also ziemlich nutzlos.
Dies bedeutet jedoch nicht, dass eine Klasse, deren Bereich Gleitkommawerte überprüft, nicht nützlich ist. Möglicherweise möchten Sie Werte auf einen bestimmten Bereich beschränken, z. B. RGB-Berechnungen.
quelle
2's complement
, gibt es kein Problem mit nicht signierten Floats?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 negativ vorzeichenbehaftete Werte.0.0
Inf oder NaN gesättigt wird . Oder seien Sie einfach undefiniertes Verhalten, wie es das OP in einer Bearbeitung der Frage vorgeschlagen hat. Re: trig-Funktionen: Definieren Sie also keine vorzeichenlosen Eingabeversionen vonsin
usw. und behandeln Sie deren Rückgabewert als signiert. Die Frage war nicht, float durch nicht signiertes float zu ersetzen, sondern nurunsigned float
als neuen Typ hinzuzufügen .(Nebenbei bemerkt, Perl 6 lässt Sie schreiben
und dann können
Nonnegative::Float
Sie wie jeden anderen Typ verwenden.)Es gibt keine Hardware-Unterstützung für vorzeichenlose Gleitkommaoperationen, daher bietet C diese nicht an. C ist meistens als "tragbare Baugruppe" konzipiert, dh so nah wie möglich am Metall, ohne an eine bestimmte Plattform gebunden zu sein.
[bearbeiten]
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 Sie müssen das explizit tun.quelle
of ...
analysiert aber nicht.Ich glaube, das nicht signierte int wurde erstellt, weil eine größere Wertmarge erforderlich ist, als das signierte int bieten könnte.
Ein Float hat einen viel größeren Spielraum, so dass es nie einen "physischen" Bedarf für einen nicht signierten Float gab. 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 bin jedoch weiterhin der Ansicht, dass dies eine Entwurfsentscheidung war, die auf den oben genannten Gründen beruhte ;-)
quelle
Ich denke, Treb ist auf dem richtigen Weg. Für Ganzzahlen ist es wichtiger, dass Sie einen entsprechenden Typ ohne Vorzeichen haben. Dies sind diejenigen, die bei der Bitverschiebung und in Bitmaps verwendet werden . Ein Zeichenstück stört gerade. Wenn Sie beispielsweise einen negativen Wert nach rechts verschieben, wird der resultierende Wert in C ++ definiert. Wenn Sie dies mit einer vorzeichenlosen Ganzzahl tun oder eine solche überlaufen, ist die Semantik perfekt definiert, da kein solches Bit im Weg ist.
Zumindest für Ganzzahlen ist die Notwendigkeit eines separaten Typs ohne Vorzeichen größer als nur das Ausgeben von Warnungen. Alle oben genannten Punkte müssen für Floats nicht berücksichtigt werden. Ich denke, es besteht kein wirklicher Bedarf an Hardware-Unterstützung für sie, und C wird sie zu diesem Zeitpunkt bereits nicht unterstützen.
quelle
C99 unterstützt komplexe Zahlen und eine generische Form von sqrt, ist also
sqrt( 1.0 * I)
negativ.Die Kommentatoren haben oben einen leichten Glanz hervorgehoben, da ich mich
sqrt
eher auf das typgenerische Makro als auf die Funktion bezog und es einen skalaren Gleitkommawert durch Abschneiden des Komplexes auf seine reale Komponente zurückgibt:Es enthält auch einen Gehirnfurz, da der Realteil des Quadrats einer komplexen Zahl positiv oder Null ist und sqrt (1,0 * I) sqrt (0,5) + sqrt (0,5) * I nicht -1,0 ist.
quelle
sqrt(-0.0)
produziert oft-0.0
. Natürlich ist -0,0 kein negativer Wert .Ich denke, es hängt davon ab, dass nur die IEEE-Gleitkomma-Spezifikationen signiert sind und dass die meisten Programmiersprachen sie verwenden.
Wikipedia-Artikel zu IEEE-754-Gleitkommazahlen
Bearbeiten: Wie von anderen angemerkt, unterstützt die meiste Hardware keine nicht negativen Floats. Daher ist die normale Art von Floats effizienter, da Hardware unterstützt wird.
quelle
Ich denke, der Hauptgrund ist, dass nicht signierte Floats im Vergleich zu nicht signierten Ints nur eine sehr begrenzte Verwendung haben würden. Ich kaufe nicht das Argument, dass es daran liegt, dass die Hardware es nicht unterstützt. Ältere Prozessoren hatten überhaupt keine Gleitkommafunktionen, alles wurde in Software emuliert. Wenn nicht signierte Floats nützlich wären, wären sie zuerst in Software implementiert worden, und die Hardware wäre diesem Beispiel gefolgt.
quelle
Ganzzahlige Typen ohne Vorzeichen in C werden so definiert, dass sie den Regeln eines abstrakten algebraischen Rings entsprechen. Wenn Sie beispielsweise für einen Wert X und Y XY zu Y hinzufügen, erhalten Sie X. Ganzzahlige Typen ohne Vorzeichen befolgen diese Regeln garantiert in allen Fällen, in denen keine Konvertierung in oder von einem anderen numerischen Typ [oder vorzeichenlose Typen unterschiedlicher Größe] erforderlich ist. und diese Garantie ist eines der wichtigsten Merkmale solcher Typen. In einigen Fällen lohnt es sich, die Darstellung negativer Zahlen im Austausch für die zusätzlichen Garantien aufzugeben, die nur nicht signierte Typen bieten können. Gleitkommatypen, ob signiert oder nicht, können nicht alle Regeln eines algebraischen Rings einhalten [z. B. können sie nicht garantieren, dass X + YY gleich X ist], und IEEE tut dies tatsächlich nicht. ' Sie dürfen nicht einmal die Regeln einer Äquivalenzklasse einhalten [indem sie verlangen, dass bestimmte Werte ungleich mit sich selbst verglichen werden]. Ich glaube nicht, dass ein "vorzeichenloser" Gleitkommatyp Axiome einhalten könnte, die ein gewöhnlicher Gleitkommatyp nicht könnte, daher bin ich mir nicht sicher, welche Vorteile er bieten würde.
quelle
IHMO liegt daran, dass die Unterstützung von vorzeichenbehafteten und vorzeichenlosen Gleitkommatypen in Hardware oder Software zu problematisch wäre
Für Integer - Typen können wir verwenden die gleiche Logik - Einheit für beide mit und ohne Vorzeichen Integer - Operationen in den meisten Fällen mit der schönen Eigenschaft von 2-Komplement, weil das Ergebnis in diesen Fällen identisch ist für add, sub, nicht-Erweiterung mul und die meisten Bit - Operationen. Für Operationen, die zwischen signierter und nicht signierter Version unterscheiden, können wir immer noch den größten Teil der Logik teilen . Beispielsweise
INT_MIN
in einen vorzeichenlosen Vergleich umgewandelt werden und umgekehrt . Auch theoretisch möglich, wird es wahrscheinlich nicht auf Hardware verwendet, aber es ist nützlich auf Systemen, die nur einen Vergleichstyp unterstützen (wie 8080 oder 8051).Systeme, die das Komplement von 1 verwenden, müssen ebenfalls nur geringfügig an der Logik geändert werden, da es sich lediglich um das Übertragsbit handelt, das auf das niedrigstwertige Bit umwickelt ist. Ich bin mir bei Vorzeichengrößen-Systemen nicht sicher, aber es scheint, als würden sie das 1-Komplement intern verwenden, sodass das Gleiche gilt
Leider haben wir diesen Luxus für Gleitkommatypen nicht. Durch einfaches Freigeben des Vorzeichenbits erhalten wir die vorzeichenlose Version. Aber wofür sollen wir dieses Bit dann verwenden?
Beide Optionen benötigen jedoch einen größeren Addierer , um den breiteren Wertebereich zu berücksichtigen . Dies erhöht die Komplexität der Logik, während das oberste Bit des Addierers die meiste Zeit unbenutzt dort sitzt. Für Multiplikationen, Divisionen oder andere komplexe Operationen werden noch mehr Schaltungen benötigt
Auf Systemen, die Software-Gleitkomma verwenden, benötigen Sie 2 Versionen für jede Funktion, die während der Zeit nicht erwartet wurden. Der Speicher war so teuer, oder Sie müssten einen "kniffligen" Weg finden, um Teile der signierten und nicht signierten Funktionen gemeinsam zu nutzen
Allerdings Gleitkomma-Hardware gab es schon lange vor dem C erfunden wurde , so dass ich die Wahl in C war wegen der fehlenden Hardware - Unterstützung , weil der Grund glaube ich oben erwähnt
Es gibt jedoch mehrere spezialisierte Gleitkommaformate ohne Vorzeichen, hauptsächlich für Bildverarbeitungszwecke, wie den 10- und 11-Bit-Gleitkommatyp der Khronos-Gruppe
quelle
Ich vermute, das liegt daran, dass die zugrunde liegenden Prozessoren, auf die C-Compiler abzielen, nicht gut mit vorzeichenlosen Gleitkommazahlen umgehen können.
quelle
Gute Frage.
Wenn es sich, wie Sie sagen, nur um Warnungen zur Kompilierungszeit und keine Änderung ihres Verhaltens handelt, ist die zugrunde liegende Hardware nicht betroffen und als solche nur eine C ++ / Compiler-Änderung.
Ich habe das gleiche schon früher gewonnen, aber die Sache ist: Es würde nicht viel helfen. Bestenfalls kann der Compiler statische Zuordnungen finden.
Oder minimalistisch länger
Aber das war es schon. Bei vorzeichenlosen Ganzzahltypen erhalten Sie auch einen definierten Wraparound, der sich wie eine modulare Arithmetik verhält.
danach hat 'uc' den Wert 255.
Was würde ein Compiler mit demselben Szenario bei einem vorzeichenlosen Float-Typ tun? Wenn die Werte zur Kompilierungszeit nicht bekannt sind, muss Code generiert werden, der zuerst die Berechnungen ausführt und dann eine Vorzeichenprüfung durchführt. Was aber, wenn das Ergebnis einer solchen Berechnung "-5,5" lautet - welcher Wert sollte in einem Float gespeichert werden, der als vorzeichenlos deklariert wurde? Man könnte modulare Arithmetik wie für integrale Typen versuchen, aber das bringt seine eigenen Probleme mit sich: Der größte Wert ist unbestreitbar unendlich ... das funktioniert nicht, man kann nicht "unendlich - 1" haben. Wenn Sie sich für den größten eindeutigen Wert entscheiden, den es halten kann, funktioniert dies auch nicht wirklich, da Sie dort auf die Präzision stoßen. "NaN" wäre ein Kandidat.
Schließlich wäre dies bei Festkommazahlen kein Problem, da Modulo gut definiert ist.
quelle