Gibt es eine Programmiersprache, in der sich 1/6 genauso verhält wie 1.0 / 6.0?

11

Während ich vor einigen Tagen in C ++ programmierte, habe ich diesen Fehler gemacht (dass ich in der Vergangenheit damit gearbeitet habe!). In einem Teil meines Codes hatte ich 1/6 und ich hatte erwartet, dass es 0,16666666666 ist, was nicht der Fall ist. Wie Sie alle wissen, ist das Ergebnis 0 - C, C ++, Java, Python, alle verhalten sich gleich.

Ich poste es auf meiner Facebook-Seite und jetzt wird diskutiert, ob es eine Programmiersprache gibt, 1/6die sich genauso verhält wie 1.0/6.0.

Pouya
quelle
5
Haskell. 1/6 = 0,166666666666666666
t123
PowerShell generiert 0.166666666666667, was mich überrascht hat, da 1 eine Ganzzahl ist. Ich würde wetten, dass es einige andere .NET-Sprachen gibt, die den erwarteten Wert generieren.
JohnL
Theoretisch gibt es eine unbegrenzte Anzahl von ihnen. Hier ist eine andere: Rebol sowie Derivate wie Orca, Red und so weiter. >> 1 / 6->== 0.166666666666667
Izkata
Lua macht das. Es gibt nur einen einzigen Zahlentyp , der normalerweise mit dem Doppel von C identisch ist.
Machado
In Clojure 1/6ist tatsächlich 1/6 (gebrochener Typ), was, Doubleerzwungen, 1,66666 ist ...
kaoD

Antworten:

17

Hat jeder Pascal vergessen?

1/6Ausbeuten 0.1666666...(mit welcher Genauigkeit auch immer unterstützt wird).

1 div 6 ergibt 0

Es ist fraglich, ob die C-Regel ein Fehler ist. Fast alle arithmetischen Operatoren von C, bei denen die Operanden vom gleichen Typ sind, ergeben ein Ergebnis vom gleichen Typ. Es gibt etwas zu sagen für die Konsistenz.

Da C hauptsächlich auf Code auf Systemebene abzielt, verwenden die meisten C-Programme überhaupt kein Gleitkomma. Zu einer Zeit könnte das versehentliche Hinzufügen von Gleitkomma-Code zu einem Programm, das es sonst nicht benötigte, ein ernstes Problem sein. Dies ist wahrscheinlich immer noch der Fall für kleine eingebettete Systeme - die wiederum ein Hauptziel für C sind.

In den meisten C-Programmen ist das Abschneiden der Ganzzahldivision wahrscheinlich genau das, was Sie sowieso wollen.

Wenn 1 / 6ein Gleitkomma-Ergebnis in C erhalten wird, dann:

  • Es wäre eine Inkonsistenz in der Sprache.
  • Der Standard müsste eine willkürliche Auswahl treffen, welcher Gleitkommatyp für das Ergebnis verwendet werden soll ( doublemag wie die natürliche Wahl erscheinen, aber Sie bevorzugen möglicherweise die zusätzliche Genauigkeit von long double).
  • Die Sprache müsste noch eine Operation für die Ganzzahldivision haben; Das Durchführen einer Gleitkommaaddition und anschließendes Abschneiden wäre wahrscheinlich nicht gut genug.

C hätte getrennte Operatoren für die beiden Arten der Division bereitstellen können, aber der zweite Punkt oben würde immer noch gelten: Welcher der drei Gleitkommatypen würde für das Ergebnis verwendet? Und da es einfach genug ist, eine Gleitkommadivision zu erhalten, wenn Sie sie benötigen (verwenden Sie eine Gleitkommakonstante für einen oder beide Operanden oder wandeln Sie einen oder beide Operanden in einen Gleitkommatyp um), war dies anscheinend nicht der Fall. Ich hielt das nicht für wichtig.

In der 1974er Version des C-Handbuchs (das ist 4 Jahre vor der Veröffentlichung der ersten Ausgabe von K & R) erwähnt Ritchie nicht einmal die mögliche Verwirrung:

Der Binär / Operator zeigt die Division an. Es gelten die gleichen Typüberlegungen wie für die Multiplikation

Das heißt, wenn beide Operanden vom Typ intoder sind char, ist das Ergebnis vom Typ int.

Ja, es ist eine Quelle der Verwirrung für einige C-Programmierer, insbesondere für Anfänger - aber C ist nicht als sehr unerfahren bekannt.

Keith Thompson
quelle
5
Pascal. Details richtig machen, bevor C sie falsch verstanden hat. ™
Mason Wheeler
Und dann war Algol eine Verbesserung gegenüber seinen Nachfolgern (in deren Anzahl sowohl C als auch Pascal stehen).
AProgrammer
Pascal - die Details richtig machen (geben oder nehmen Sie einen Faktor von 10)
Martin Beckett
1
Ich habe ursprünglich geschrieben 1.666666..., was eindeutig falsch ist. Meine lahme Entschuldigung ist, dass das Pascal-Testprogramm, das ich geschrieben habe, gedruckt wurde1.6666666666666667E-0001
Keith Thompson
16

Tatsächlich wurde dieses Verhalten in Python 3 geändert und verhält sich jetzt wie erwartet ( //wird jetzt für die Ganzzahldivision verwendet).

sepp2k
quelle
Vielen Dank. Haben Sie noch andere Programmiersprachen im Kopf? Grundfamilie vielleicht?
Pouya
1
@Pouya: Dieses Verhalten ist Standard für die Pascal-Familie. /Erzeugt immer einen Gleitkommawert und ein separater Operator ( div) wird für die Ganzzahldivision verwendet.
Mason Wheeler
13

Aus bekannten Sprachen JavaScript. 1,0 / 6,0 = 1/6 = 0,16666666666666666.

Ich sehe das nicht als überraschend an. Als Faustregel gilt: Wenn eine Sprache zwischen numerischen Ganzzahl- und Gleitkommatypen unterscheidet, ergibt das Teilen von zwei Ganzzahlen eine abgeschnittene Ganzzahl anstelle von Gleitkommazahlen. Wenn dies nicht der Fall ist, werden höchstwahrscheinlich standardmäßig Gleitkommaoperationen verwendet. Dies sollte das erwartete Verhalten des Programmierers sein.

Denken Sie daran, dass hier noch weitere Dinge im Spiel sein könnten, wie der bereits erwähnte separate Ganzzahl-Divisionsoperator oder das implizite Typ-Casting.

scrwtp
quelle
6
Dies ist keine "Faustregel". Es ist eine Regel von C und eine Handvoll Sprachen, die blind Cs Fehler kopiert haben.
Mason Wheeler
4
@ MasonWheeler: Hat FORTRAN "Cs Fehler blind kopiert"? Ich persönlich bin der Meinung, dass gut gestaltete Sprachen separate Operatoren für die abgeschnittene Division im Vergleich zur ungefähren Division ganzzahliger Mengen (und übrigens auch für die Wert- und Referenzgleichheit) verwenden sollten, aber die Entwurfsentscheidung in den Tagen von FORTRAN war wahrscheinlich vernünftig. Das bedeutet nicht, dass jede Sprache die Dinge so tun sollte, wie es FORTRAN in den 1950er Jahren getan hat.
Supercat
3
Niedrigere Sprachen müssen diese Unterscheidungen treffen. Höhere / dynamische Sprachen sind ohne sie oft besser dran. Es ist ein Design-Kompromiss. Ich schätze es, nur einen großen dummen Zahlenkonstruktor / -typ in JS zu haben, aber ich kann mir vorstellen, dass JS beim Versuch, eine Hochleistungs-3D-Engine ohne strengere Typsteuerung zu schreiben, fehlt. JS hat möglicherweise ein überlappendes Dienstprogramm mit anderen Sprachen, aber ich glaube nicht, dass irgendjemand es jemals als ein Muss betrachten wird, um Hochleistungsmaterial zu schreiben, das näher am Chrom liegt.
Erik Reppen
2
Mathematik außerhalb der Programmierung berücksichtigt nur ganzzahlige Regeln für die Division.
Erik Reppen
5
@ MasonWheeler: Programmieren ist keine reine Mathematik. Mathematisch gesehen ist 1/6 eine rationale Zahl und kann nicht genau durch eine binäre Gleitkommazahl dargestellt werden. Die einzige genaue Darstellung ist ein Verhältnis mit einem Nenner, der sechsmal so groß ist wie der Zähler.
Kevin Cline
7

Es gibt viele Sprachen, in denen ((1/6)*6)1 und nicht 0 resultiert. Zum Beispiel PL / SQL, viele BASIC-Dialekte, Lua.

In all diesen Sprachen ergibt 1/6 versehentlich .166666667 oder 0.16666666666667 oder ähnliches. Ich habe die Variante ((1/6) * 6) == 1 gewählt, um diese kleinen Unterschiede zu beseitigen.

user281377
quelle
7
Das ist nicht die Frage.
Roc Martí
20
In all diesen Sprachen ergibt 1/6 versehentlich .166666667 oder 0.16666666666667 oder ähnliches. Ich habe die ((1/6)*6)==1Variante gewählt, um diese kleinen Unterschiede zu beseitigen, aber es sieht so aus, als hätte ich die mathematischen Fähigkeiten einiger Leute überschätzt.
user281377
1
@ RocMartí ja, es ist wirklich ...
MattDavey
1
Ich wäre überrascht zu sehen, dass (1.0 / 6.0) * 6 genau gleich 1 ist! Die Rundung des Ergebnisses von (1.0 / 6.0) führt zu einem kleinen Unterschied. (Obwohl es einige Sprachen geben wird, die standardmäßig unendlich präzise sind)
Sjoerd
1
@Sjoerd: Es ist nicht allzu überraschend, dass es genau ist. Betrachten Sie das Szenario von 1/11 * 11 dezimal mit allen Werten, die auf fünf signifikante Zahlen genau sind. Der Wert von 1/11 ist 9.0909 * 10 ^ -2. Multiplizieren Sie mit 11 und Sie erhalten 99,9999 * 10 / -2 vor dem Runden. Runden Sie auf fünf signifikante Zahlen und das Ergebnis wird 1,0000 * 10 ^ 0 sein. Beachten Sie, dass der Schlüssel darin besteht, dass die Mantisse von 1/6 "... 0101010101 ..." ist. Wenn das letzte Bit einer Darstellung eine "1" ist, ergibt das Multiplizieren mit sechs und das Runden 1. Wenn das letzte Bit Null wäre, wäre dies nicht der Fall.
Supercat
3

Haskell behandelt 1/6 und 1,0 / 6,0 als identisch 0,16666666666666666. Außerdem werden 1 / 6.0 und 1.0 / 6 als der gleiche Wert dargestellt.

Dies liegt daran, dass die grundlegenden numerischen Typen in Haskell nicht ganz mit denen anderer Sprachen identisch sind. Wahre Ganzzahldivision ist etwas ... kompliziert.

Weltingenieur
quelle
2

Ja, Perl tut es. Der Einzeiler

perl -e '$x=1/6;print "$x\n";'

ergibt die Ausgabe von:

0.166666666666667

Ich glaube, dass PHP genauso funktioniert.

Bearbeitet, um hinzuzufügen: Ich glaube auch, dass eine notwendige (aber nicht ausreichende) Bedingung dafür 1/6 == 1.0/6.0ist, dass die betreffende Sprache schwach getippt ist.


quelle
Warum zum Teufel sollte schwaches Tippen (was auch immer das bedeuten soll) notwendig sein? Definieren Sie einfach / um (auch) die Float-Division zu bedeuten, wenn beide Argumente Ganzzahlen sind.
1
@delnan - en.wikipedia.org/wiki/Weak_typing Ich nehme an, dass es möglich sein könnte, eine stark typisierte Sprache zu haben, in der /abhängig von den Arten der Argumente automatisch überladen wird, aber das scheint eine Verletzung des Prinzips des geringsten Erstaunens zu sein ich ...
2
Schwache / starke Eingabe ist schlecht definiert (wie das Wiki auch impliziert), bitte vermeiden Sie es und seien Sie genau. Ich nehme an, Ihre Definition verbietet implizite Konvertierung, aber keinen Ad-hoc-Polymorphismus? Wenn ja, betrachten Sie Haskell, das keine impliziten Konvertierungen aufweist, aber ziemlich gut ausgeführt ist (wie in, arbeitet 99% der Zeit und kann von Sterblichen verstanden werden), numerischen Polymorphismus. Und warum sollte das erstaunlich sein? Es wäre weitaus erstaunlicher (um nicht zu sagen ärgerlich) für mich, wenn ich jeder einzelnen Instanz eines Operators eine Anzahl von Punkten hinzufügen müsste, abhängig von der genauen Genauigkeit, die ich wünsche.
2
@ JackManey Ich denke, für die meisten Neulinge ist es viel überraschender, dass 1/2 gleich 0 sein sollte, als wenn das Teilen von zwei ganzen Zahlen zu einem Doppel führt. Schließlich werden ganze Zahlen auch in Mathematik nicht unter Division geschlossen. Wie Delnan betont, ist Haskell ein Beispiel für eine stark typisierte Sprache, in der / auf zwei ganzen Zahlen keine ganze Zahl erzeugt wird. Und Python 3 ist eine andere.
sepp2k
1
Die Haxe-Sprache ist stark (wenn auch abgeleitet) typisiert und hat dennoch keine ganzzahlige Unterteilung, sondern nur Float. Hier bitteschön.
K.Steff
2

In Squeak Smalltalk /für Ganzzahlen werden Bruchobjekte erstellt. Dies ist zwar nicht dasselbe wie die Float-Division, gibt aber dennoch (1/6)*61 zurück.

Kopffüßer
quelle
In allen Smalltalk-80-Derivaten (dh fast allen Smalltalks). Bernstein ist eine der zeitgenössischen Ausnahmen (was verständlich ist, wenn es zu JavaScript kompiliert wird).
Herby
2

Ja, ich habe gerade die in TI BASIC eingebauten TI-99 / 4A überprüft . Da alle numerischen Ausdrücke als Gleitkomma behandelt werden, ist die Divisionsoperation auch Gleitkomma.

 TI BASIC READY
>PRINT 1/6
  .1666666667

>
Jesse C. Slicer
quelle
2

VB ( VB.Net , VB6 , VBA ...)

Der ganzzahlige Divisionsoperator ist \

MarkJ
quelle
2

MATLAB. Numerische Literale sind standardmäßig doppelt.

>> 1/6
ans =
    0.1667
Dima
quelle
2

Clojure verwendet standardmäßig Brüche. Es ist nicht dasselbe wie 1.0 / 6.0, aber Sie können es mit floatoder doublebei Bedarf konvertieren .

user=> (/ 1 6)
1/6
user=> (* (/ 1 6) 2)
1/3
user=> (pos? (/ 1 6)) ; Is 1/6 > 0?
true
user=> (float (/ 1 6))
0.16666667
defhlt
quelle
1

Überraschenderweise scheint es in Windows PowerShell (Version 3) ordnungsgemäß zu funktionieren .

PS C:\> 1.0 / 6.0
0.166666666666667

PS C:\> 1/6
0.166666666666667

Scheint auch in Python 3 zu funktionieren, wie sepp2k erwähnt. Die beiden anderen Sprachen, die ich für REPL zur Verfügung habe, Scala und Ruby, machen beide eine ganzzahlige Division und ergeben 0.

KChaloux
quelle
0

Die Rexx-Sprache liefert immer eine arithmetisch korrekte Antwort. Zum Beispiel: 5/2 = 2,5. Rexx ist eine großartige Sprache, die nicht genug verwendet wurde. Wenn ein Compiler nicht bestimmen kann, was Sie wollen, ist es theoretisch besser, die richtige Mathematik durchzuführen. Dies ist jedoch möglicherweise nicht effizient. Rexx stellt auch den Operator // bereit.

Keine Chance
quelle