Inkonsistente Ergebnisse mit exAllArithmeticExceptions in Win32 und Win64

8

Ein Kollege von mir hat eine Diskrepanz zwischen Win32- und Win64-Code entdeckt, die von Delphi in der Handhabung von NaNs kompiliert wurden. Nehmen Sie den folgenden Code als Beispiel. Wenn wir in 32 Bit kompiliert werden, erhalten wir keine Nachrichten, aber wenn wir mit 64 Bit kompiliert werden, erhalten beide Vergleiche Wahr.

%Vor%

Mein Verständnis des IEEE-Standards ist, dass & lt; & gt; sollte true zurückgeben, aber alle anderen Operationen sollten false zurückgeben. In diesem Fall sieht es so aus, als ob die 64-Bit-Version korrekt ist und die 32-Bit-Version nicht korrekt ist. Der von beiden generierte Code ist bei der 64-Bit-Version, die SSE-Code generiert, sehr unterschiedlich.

Für 32 Bit:

%Vor%

und für 64 bit:

%Vor%

Meine Frage ist das. Ist das ein Problem mit dem Delphi-Compiler oder ein Vorbehalt mit der Intel-CPU?

    
Graymatter 23.10.2017, 19:23
quelle

1 Antwort

4

Der IEEE-754-Standard definiert arithmetische Formate, Operationen, Rundungsregeln, Ausnahmen usw. für die Fließkommaberechnung. Der Delphi-Compiler implementiert Gleitkommaarithmetik über den verfügbaren Hardwareeinheiten. Für den 32-Bit-Windows-Compiler ist dies die x87-Einheit, und für den 64-Bit-Windows-Compiler ist dies die SSE-Einheit. Beide Hardwareeinheiten entsprechen dem IEEE 754-Standard.

Der Unterschied, den Sie beobachten, entsteht auf der Ebene der Sprachumsetzung. Sehen wir uns die beiden Versionen genauer an.

32-Bit-Windows-Compiler

Die Vergleichsaussage ist dazu zusammengestellt:

%Vor%

Das Intel Software-Entwicklerhandbuch sagt, dass ein ungeordneter Vergleich durch die Flags C3, C2 und C0 angezeigt wird, die auf 1 gesetzt sind. Die vollständige Tabelle ist hier:

%Vor%

Wenn Sie die FPU unter dem Debugger untersuchen, können Sie sehen, dass dies der Fall ist.

%Vor%

Damit werden verschiedene Bits aus dem FPU-Statusregister in die CPU-Flags übertragen. Detaillierte Informationen dazu, welche Flags wohin gehen, finden Sie im Handbuch. Die Verzweigung wird gemacht, wenn ZF gesetzt ist. Der Wert von ZF kommt von dem C3-FPU-Flag, das, wie in der obigen Tabelle gezeigt, für den ungeordneten Fall eingestellt ist.

Tatsächlich kann der gesamte Verzweigungscode im Pseudocode wie folgt ausgedrückt werden:

%Vor%

Wenn wir uns also die obige Tabelle ansehen, ist klar, dass, wenn einer der Operanden ein NaN ist, jeder Gleitkomma-Gleichheitsvergleich als gleich ausgewertet wird.

64-Bit-Windows-Compiler

Die Vergleichsaussage ist dazu zusammengestellt:

%Vor%

Der Vergleich wird mit der Anweisung ucomisd durchgeführt. Das Handbuch gibt diesen Pseudo-Code:

%Vor%

Beachten Sie, dass in dieser Anweisung die ZF-, PF- und CF-Flags genau analog zu den C3-, C2- und C0-Flags auf der x87-Einheit sind.

Die Verzweigung wird von diesem Code behandelt:

%Vor%

Beachten Sie, dass zuerst der Paritätsflag PF (der jp -Befehl) und dann der Nullflag ZF (der jz -Befehl) getestet wird. Der Compiler hat daher Code ausgegeben, um den ungeordneten Fall zu handhaben (d. H. Einer der Operanden ist NaN). Dies wird zuerst mit dem jp behandelt. Sobald dies erledigt ist, prüft der Compiler das Null-Flag ZF, das (weil NaNs behandelt wurden) genau dann gesetzt wird, wenn die beiden Operanden gleich sind.

Fazit

Das unterschiedliche Verhalten hängt von den verschiedenen Compilern ab, die bei der Implementierung der Vergleichsoperatoren unterschiedliche Entscheidungen treffen. In beiden Fällen ist die Hardware IEEE 754-konform und in der Lage, NaNs wie vom Standard spezifiziert zu vergleichen.

Meine beste Vermutung wäre, dass die Entscheidungen für den 32-Bit-Compiler vor sehr langer Zeit getroffen wurden. Einige dieser Entscheidungen sind fraglich. Aus meiner Sicht sollte ein Gleichheitsvergleich mit einem NaN-Operanden unabhängig vom anderen Operanden nicht gleichwertig sein. Das Gewicht der Geschichte, das durch den Wunsch nach Rückwärtskompatibilität gespürt wird, bedeutet, dass diese fragwürdigen Entscheidungen nie in Angriff genommen wurden.

Als der 64-Bit-Compiler erstellt wurde, entschieden sich die Embarcadero-Ingenieure in letzter Zeit, einige dieser Fehler zu korrigieren. Sie hatten vermutlich das Gefühl, dass der Durchbruch zu einer neuen Architektur ihnen die Freiheit gab, dies zu tun.

In einer idealen Welt könnte der 32-Bit-Compiler so konfiguriert werden, dass er sich genauso verhält wie der 64-Bit-Compiler, indem er einen Compiler-Schalter setzt.

    
David Heffernan 24.10.2017, 09:19
quelle

Tags und Links