C # -Überlaufverhalten für deaktiviertes uint

10

Ich habe diesen Code unter https://dotnetfiddle.net/ getestet :

using System;

public class Program
{
    const float scale = 64 * 1024;

    public static void Main()
    {
        Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale)));
        Console.WriteLine(unchecked((uint)(ulong)(scale* scale + 7)));
    }
}

Wenn ich mit .NET 4.7.2 kompiliere, bekomme ich

859091763

7

Aber wenn ich Roslyn oder .NET Core mache, bekomme ich

859091763

0

Warum passiert das?

Lukas
quelle
Die Umwandlung ulongin wird im letzteren Fall ignoriert, sodass dies bei der Konvertierung float-> geschieht int.
Madreflection
Ich bin mehr überrascht von der Änderung des Verhaltens, das scheint ein ziemlich großer Unterschied zu sein. Ich würde nicht erwarten, dass "0" auch mit dieser Kette von Besetzungen eine gültige Antwort ist.
Lukas
Verständlich. Einige Dinge in der Spezifikation wurden im Compiler behoben, als sie Roslyn bauten, so dass dies ein Teil davon sein könnte. Überprüfen Sie die JIT-Ausgabe dieser Version in SharpLab. Das zeigt, wie sich die Besetzung ulongauf das Ergebnis auswirkt.
Madreflection
Es ist faszinierend, mit Ihrem Beispiel auf dotnetfiddle, dass die letzte WriteLine 0 in Roslyn 3.4 und 7 in .NET Core 3.1 ausgibt
Lukas
Ich habe auch auf meinem Desktop bestätigt. Der JIT-Code sieht überhaupt nicht genau aus, ich erhalte unterschiedliche Ergebnisse zwischen .NET Core und .NET Framework. Trippy
Lukas

Antworten:

1

Meine Schlussfolgerungen waren falsch. Weitere Informationen finden Sie im Update.

Sieht aus wie ein Fehler im ersten Compiler, den Sie verwendet haben. Null ist in diesem Fall das richtige Ergebnis . Die durch die C # -Spezifikation vorgegebene Reihenfolge der Operationen ist wie folgt:

  1. multiplizieren scalemit scale, ergibta
  2. durchführen a + 7, nachgebenb
  3. gegossen bzu ulong, wodurch manc
  4. gegossen czu uint, wodurch mand

Bei den ersten beiden Operationen erhalten Sie einen Gleitkommawert von b = 4.2949673E+09f. Unter Standard-Gleitkomma-Arithmetik ist dies 4294967296( Sie können es hier überprüfen ). Das paßt in ulongganz gut, so c = 4294967296, aber es ist genau ein mehr als uint.MaxValue, so dass es Rundfahrten zu 0, daher d = 0. Nun, was für eine Überraschung, da Gleitkommaarithmetik ist funky, 4.2949673E+09fund 4.2949673E+09f + 7ist genau die gleiche Anzahl in IEEE 754. So scale * scalegibt Ihnen den gleichen Wert ein floatwie scale * scale + 7, a = b, so dass die zweite Operation ist im Grunde ein no-op.

Der Roslyn-Compiler führt zur Kompilierungszeit (einige) const-Operationen aus und optimiert diesen gesamten Ausdruck auf 0. Auch dies ist das richtige Ergebnis , und der Compiler darf alle Optimierungen durchführen, die genau das gleiche Verhalten wie der Code ohne sie ergeben.

Ich vermute, dass der von Ihnen verwendete .NET 4.7.2-Compiler ebenfalls versucht, dies zu optimieren, aber einen Fehler aufweist, der dazu führt, dass die Besetzung an einer falschen Stelle ausgewertet wird. Natürlich, wenn Sie zuerst gegossen scalezu ein uintund dann die Operation durchführen, erhalten Sie 7, denn scale * scaleUmläufe zu 0und dann Sie hinzufügen 7. Dies steht jedoch im Widerspruch zu dem Ergebnis, das Sie erhalten würden, wenn Sie die Ausdrücke zur Laufzeit Schritt für Schritt auswerten würden . Auch hier ist die Grundursache nur eine Vermutung, wenn man sich das erzeugte Verhalten ansieht, aber angesichts all dessen, was ich oben angegeben habe, bin ich überzeugt, dass dies eine Spezifikationsverletzung auf der Seite des ersten Compilers ist.

AKTUALISIEREN:

Ich habe einen Trottel gemacht. Es gibt dieses Stück der C # -Spezifikation , von dem ich nicht wusste, dass es beim Schreiben der obigen Antwort existiert:

Gleitkommaoperationen können mit höherer Genauigkeit als der Ergebnistyp der Operation ausgeführt werden. Beispielsweise unterstützen einige Hardwarearchitekturen einen "erweiterten" oder "langen doppelten" Gleitkommatyp mit größerer Reichweite und Genauigkeit als der doppelte Typ und führen implizit alle Gleitkommaoperationen mit diesem Typ mit höherer Genauigkeit aus. Nur mit übermäßigen Leistungskosten können solche Hardwarearchitekturen dazu gebracht werden, Gleitkommaoperationen mit geringerer Genauigkeit auszuführen, und anstatt eine Implementierung zu erfordern, die sowohl Leistung als auch Präzision einbüßt, ermöglicht C # die Verwendung eines Typs mit höherer Genauigkeit für alle Gleitkommaoperationen . Abgesehen von genaueren Ergebnissen hat dies selten messbare Auswirkungen. In Ausdrücken der Form x * y / z,

C # garantiert, dass Operationen mindestens auf der Ebene von IEEE 754 ein Maß an Präzision bieten , aber nicht unbedingt genau das. Es ist kein Fehler, es ist eine Spezifikationsfunktion. Der Roslyn Compiler ist in seinem Recht , den Ausdruck genau nach IEEE 754 angibt und der andere Compiler ist in ihrem rechten herzuleiten zu bewerten , die 2^32 + 7sich , 7wenn in setzen uint.

Es tut mir leid für meine irreführende erste Antwort, aber zumindest haben wir heute alle etwas gelernt.

V0ldek
quelle
Dann haben wir wohl einen Fehler im aktuellen .NET Framework-Compiler (ich habe es gerade in VS 2019 versucht, nur um sicherzugehen) :) Ich denke, ich werde versuchen zu sehen, ob es einen Ort gibt, an dem ein Fehler protokolliert werden kann, obwohl so etwas behoben würde haben wahrscheinlich viele unerwünschte Nebenwirkungen und werden wahrscheinlich ignoriert ...
Lukas
Ich denke nicht, dass es vorzeitig in int umgewandelt wird, was in vielen Fällen viel klarere Probleme verursacht hätte. Ich denke, der Fall hier ist, dass es in der const-Operation den Wert nicht bewertet und ihn bis zur allerletzten Bedeutung umwandelt Anstatt die Zwischenwerte in Floats zu speichern, wird dies nur übersprungen und in jedem Ausdruck durch den Ausdruck selbst ersetzt
jalsh
@jalsh Ich glaube nicht, dass ich deine Vermutung verstehe. Wenn der Compiler einfach jeden scaledurch den Gleitkommawert ersetzen und dann zur Laufzeit alles andere auswerten würde, wäre das Ergebnis dasselbe. Können Sie das näher erläutern?
V0ldek
@ V0ldek, die Abwertung war ein Fehler, ich habe deine Antwort bearbeitet, damit ich sie entfernen kann :)
jalsh
Ich vermute, dass es die Zwischenwerte nicht in Floats gespeichert hat, sondern nur f durch den Ausdruck ersetzt, der f auswertet, ohne es in float zu
verwandeln
0

Der Punkt hier ist (wie Sie in den Dokumenten sehen können ), dass Float- Werte nur eine Basis von bis zu 2 ^ 24 haben können . Wenn Sie also einen Wert von 2 ^ 32 zuweisen ( 64 * 2014 * 164 * 1024 = 2 ^ 6 * 2 ^ 10 * 2 ^ 6 * 2 ^ 10 = 2 ^ 32 ), wird er tatsächlich 2 ^ 24 * 2 ^ 8 , das ist 4294967000 . Das Hinzufügen von 7 wird nur zu dem Teil hinzugefügt, der durch Konvertierung in ulong abgeschnitten wurde .

Wenn Sie zu double wechseln , das eine Basis von 2 ^ 53 hat , funktioniert es für das, was Sie wollen.

Dies kann ein Laufzeitproblem sein, in diesem Fall handelt es sich jedoch um ein Problem zur Kompilierungszeit, da alle Werte Konstanten sind und vom Compiler ausgewertet werden.

Paulo Morgado
quelle
-2

Zunächst verwenden Sie den ungeprüften Kontext, eine Anweisung für den Compiler. Als Entwickler sind Sie sicher, dass das Ergebnis nicht überläuft und Sie keinen Kompilierungsfehler sehen möchten. In Ihrem Szenario ist der Typ absichtlich überfüllt und Sie erwarten ein konsistentes Verhalten auf drei verschiedenen Compilern, von denen einer im Vergleich zu Roslyn und .NET Core, die neu sind, wahrscheinlich weit abwärtskompatibel zum Verlauf ist.

Zweitens mischen Sie implizite und explizite Konvertierungen. Ich bin mir über den Roslyn-Compiler nicht sicher, aber definitiv können .NET Framework- und .NET Core-Compiler unterschiedliche Optimierungen für diese Vorgänge verwenden.

Das Problem hierbei ist, dass in der ersten Zeile Ihres Codes nur Gleitkommawerte / -typen verwendet werden, in der zweiten Zeile jedoch eine Kombination aus Gleitkommawerten / -typen und Integralwert / -typ.

Wenn Sie sofort einen ganzzahligen Gleitkommatyp (7> 7.0) erstellen, erhalten Sie für alle drei kompilierten Quellen das gleiche Ergebnis.

using System;

public class Program
{
    const float scale = 64 * 1024;

    public static void Main()
    {
        Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale))); // 859091763
        Console.WriteLine(unchecked((uint)(ulong)(scale * scale + 7.0))); // 7
    }
}

Ich würde also das Gegenteil von der Antwort von V0ldek sagen und das ist "Der Fehler (wenn es wirklich ein Fehler ist) ist höchstwahrscheinlich in Roslyn- und .NET Core-Compilern".

Ein weiterer Grund zu der Annahme, dass das Ergebnis der ersten ungeprüften Berechnungsergebnisse für alle gleich ist und der Wert den Maximalwert des UInt32Typs überschreitet .

Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale) - UInt32.MaxValue - 1)); // 859091763

Minus eins ist da, wenn wir bei Null beginnen, was ein Wert ist, der sich nur schwer selbst subtrahieren lässt. Wenn mein mathematisches Verständnis des Überlaufs korrekt ist, beginnen wir mit der nächsten Zahl nach dem Maximalwert.

AKTUALISIEREN

Nach dem Jalsh-Kommentar

7.0 ist ein Double, kein Float. Versuchen Sie 7.0f, es gibt Ihnen immer noch eine 0

Sein Kommentar ist richtig. Wenn wir float verwenden, erhalten Sie immer noch 0 für Roslyn und .NET Core, andererseits verwenden Sie doppelte Ergebnisse in 7.

Ich habe einige zusätzliche Tests gemacht und die Dinge werden noch seltsamer, aber am Ende macht alles Sinn (zumindest ein bisschen).

Ich gehe davon aus, dass der .NET Framework 4.7.2-Compiler (veröffentlicht Mitte 2018) wirklich andere Optimierungen verwendet als die .NET Core 3.1- und Roslyn 3.4-Compiler (veröffentlicht Ende 2019). Diese verschiedenen Optimierungen / Berechnungen werden ausschließlich für konstante Werte verwendet, die zur Kompilierungszeit bekannt sind. Aus diesem Grund musste das uncheckedSchlüsselwort verwendet werden, da der Compiler bereits weiß, dass ein Überlauf auftritt. Zur Optimierung der endgültigen IL wurden jedoch andere Berechnungen verwendet.

Gleicher Quellcode und fast dieselbe IL mit Ausnahme der Anweisung IL_000a. Ein Compiler berechnet 7 und der andere 0.

Quellcode

using System;

public class Program
{
    const float scale = 64 * 1024;

    public static void Main()
    {
        Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale)));
        Console.WriteLine(unchecked((uint)(scale * scale + 7.0)));
    }
}

.NET Framework (x64) IL

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
    extends [mscorlib]System.Object
{
    // Fields
    .field private static literal float32 scale = float32(65536)

    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 17 (0x11)
        .maxstack 8

        IL_0000: ldc.i4 859091763
        IL_0005: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_000a: ldc.i4.7
        IL_000b: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_0010: ret
    } // end of method Program::Main

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2062
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ret
    } // end of method Program::.ctor

} // end of class Program

Roslyn-Compiler-Zweig (September 2019) IL

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
    extends [System.Private.CoreLib]System.Object
{
    // Fields
    .field private static literal float32 scale = float32(65536)

    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 17 (0x11)
        .maxstack 8

        IL_0000: ldc.i4 859091763
        IL_0005: call void [System.Console]System.Console::WriteLine(uint32)
        IL_000a: ldc.i4.0
        IL_000b: call void [System.Console]System.Console::WriteLine(uint32)
        IL_0010: ret
    } // end of method Program::Main

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2062
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
        IL_0006: ret
    } // end of method Program::.ctor

} // end of class Program

Es beginnt den richtigen Weg zu gehen, wenn Sie nicht konstante Ausdrücke (standardmäßig unchecked) wie unten hinzufügen .

using System;

public class Program
{
    static Random random = new Random();

    public static void Main()
    {
        var scale = 64 * random.Next(1024, 1025);       
        uint f = (uint)(ulong)(scale * scale + 7f);
        uint d = (uint)(ulong)(scale * scale + 7d);
        uint i = (uint)(ulong)(scale * scale + 7);

        Console.WriteLine((uint)(ulong)(1.2 * scale * scale + 1.5 * scale)); // 859091763
        Console.WriteLine((uint)(ulong)(scale * scale + 7f)); // 7
        Console.WriteLine(f); // 7
        Console.WriteLine((uint)(ulong)(scale * scale + 7d)); // 7
        Console.WriteLine(d); // 7
        Console.WriteLine((uint)(ulong)(scale * scale + 7)); // 7
        Console.WriteLine(i); // 7
    }
}

Was von beiden Compilern "genau" dieselbe IL erzeugt.

.NET Framework (x64) IL

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
    extends [mscorlib]System.Object
{
    // Fields
    .field private static class [mscorlib]System.Random random

    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 164 (0xa4)
        .maxstack 4
        .locals init (
            [0] int32,
            [1] uint32,
            [2] uint32
        )

        IL_0000: ldc.i4.s 64
        IL_0002: ldsfld class [mscorlib]System.Random Program::random
        IL_0007: ldc.i4 1024
        IL_000c: ldc.i4 1025
        IL_0011: callvirt instance int32 [mscorlib]System.Random::Next(int32, int32)
        IL_0016: mul
        IL_0017: stloc.0
        IL_0018: ldloc.0
        IL_0019: ldloc.0
        IL_001a: mul
        IL_001b: conv.r4
        IL_001c: ldc.r4 7
        IL_0021: add
        IL_0022: conv.u8
        IL_0023: conv.u4
        IL_0024: ldloc.0
        IL_0025: ldloc.0
        IL_0026: mul
        IL_0027: conv.r8
        IL_0028: ldc.r8 7
        IL_0031: add
        IL_0032: conv.u8
        IL_0033: conv.u4
        IL_0034: stloc.1
        IL_0035: ldloc.0
        IL_0036: ldloc.0
        IL_0037: mul
        IL_0038: ldc.i4.7
        IL_0039: add
        IL_003a: conv.i8
        IL_003b: conv.u4
        IL_003c: stloc.2
        IL_003d: ldc.r8 1.2
        IL_0046: ldloc.0
        IL_0047: conv.r8
        IL_0048: mul
        IL_0049: ldloc.0
        IL_004a: conv.r8
        IL_004b: mul
        IL_004c: ldc.r8 1.5
        IL_0055: ldloc.0
        IL_0056: conv.r8
        IL_0057: mul
        IL_0058: add
        IL_0059: conv.u8
        IL_005a: conv.u4
        IL_005b: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_0060: ldloc.0
        IL_0061: ldloc.0
        IL_0062: mul
        IL_0063: conv.r4
        IL_0064: ldc.r4 7
        IL_0069: add
        IL_006a: conv.u8
        IL_006b: conv.u4
        IL_006c: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_0071: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_0076: ldloc.0
        IL_0077: ldloc.0
        IL_0078: mul
        IL_0079: conv.r8
        IL_007a: ldc.r8 7
        IL_0083: add
        IL_0084: conv.u8
        IL_0085: conv.u4
        IL_0086: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_008b: ldloc.1
        IL_008c: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_0091: ldloc.0
        IL_0092: ldloc.0
        IL_0093: mul
        IL_0094: ldc.i4.7
        IL_0095: add
        IL_0096: conv.i8
        IL_0097: conv.u4
        IL_0098: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_009d: ldloc.2
        IL_009e: call void [mscorlib]System.Console::WriteLine(uint32)
        IL_00a3: ret
    } // end of method Program::Main

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2100
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ret
    } // end of method Program::.ctor

    .method private hidebysig specialname rtspecialname static 
        void .cctor () cil managed 
    {
        // Method begins at RVA 0x2108
        // Code size 11 (0xb)
        .maxstack 8

        IL_0000: newobj instance void [mscorlib]System.Random::.ctor()
        IL_0005: stsfld class [mscorlib]System.Random Program::random
        IL_000a: ret
    } // end of method Program::.cctor

} // end of class Program

Roslyn-Compiler-Zweig (September 2019) IL

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
    extends [System.Private.CoreLib]System.Object
{
    // Fields
    .field private static class [System.Private.CoreLib]System.Random random

    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 164 (0xa4)
        .maxstack 4
        .locals init (
            [0] int32,
            [1] uint32,
            [2] uint32
        )

        IL_0000: ldc.i4.s 64
        IL_0002: ldsfld class [System.Private.CoreLib]System.Random Program::random
        IL_0007: ldc.i4 1024
        IL_000c: ldc.i4 1025
        IL_0011: callvirt instance int32 [System.Private.CoreLib]System.Random::Next(int32, int32)
        IL_0016: mul
        IL_0017: stloc.0
        IL_0018: ldloc.0
        IL_0019: ldloc.0
        IL_001a: mul
        IL_001b: conv.r4
        IL_001c: ldc.r4 7
        IL_0021: add
        IL_0022: conv.u8
        IL_0023: conv.u4
        IL_0024: ldloc.0
        IL_0025: ldloc.0
        IL_0026: mul
        IL_0027: conv.r8
        IL_0028: ldc.r8 7
        IL_0031: add
        IL_0032: conv.u8
        IL_0033: conv.u4
        IL_0034: stloc.1
        IL_0035: ldloc.0
        IL_0036: ldloc.0
        IL_0037: mul
        IL_0038: ldc.i4.7
        IL_0039: add
        IL_003a: conv.i8
        IL_003b: conv.u4
        IL_003c: stloc.2
        IL_003d: ldc.r8 1.2
        IL_0046: ldloc.0
        IL_0047: conv.r8
        IL_0048: mul
        IL_0049: ldloc.0
        IL_004a: conv.r8
        IL_004b: mul
        IL_004c: ldc.r8 1.5
        IL_0055: ldloc.0
        IL_0056: conv.r8
        IL_0057: mul
        IL_0058: add
        IL_0059: conv.u8
        IL_005a: conv.u4
        IL_005b: call void [System.Console]System.Console::WriteLine(uint32)
        IL_0060: ldloc.0
        IL_0061: ldloc.0
        IL_0062: mul
        IL_0063: conv.r4
        IL_0064: ldc.r4 7
        IL_0069: add
        IL_006a: conv.u8
        IL_006b: conv.u4
        IL_006c: call void [System.Console]System.Console::WriteLine(uint32)
        IL_0071: call void [System.Console]System.Console::WriteLine(uint32)
        IL_0076: ldloc.0
        IL_0077: ldloc.0
        IL_0078: mul
        IL_0079: conv.r8
        IL_007a: ldc.r8 7
        IL_0083: add
        IL_0084: conv.u8
        IL_0085: conv.u4
        IL_0086: call void [System.Console]System.Console::WriteLine(uint32)
        IL_008b: ldloc.1
        IL_008c: call void [System.Console]System.Console::WriteLine(uint32)
        IL_0091: ldloc.0
        IL_0092: ldloc.0
        IL_0093: mul
        IL_0094: ldc.i4.7
        IL_0095: add
        IL_0096: conv.i8
        IL_0097: conv.u4
        IL_0098: call void [System.Console]System.Console::WriteLine(uint32)
        IL_009d: ldloc.2
        IL_009e: call void [System.Console]System.Console::WriteLine(uint32)
        IL_00a3: ret
    } // end of method Program::Main

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2100
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
        IL_0006: ret
    } // end of method Program::.ctor

    .method private hidebysig specialname rtspecialname static 
        void .cctor () cil managed 
    {
        // Method begins at RVA 0x2108
        // Code size 11 (0xb)
        .maxstack 8

        IL_0000: newobj instance void [System.Private.CoreLib]System.Random::.ctor()
        IL_0005: stsfld class [System.Private.CoreLib]System.Random Program::random
        IL_000a: ret
    } // end of method Program::.cctor

} // end of class Program

Letztendlich glaube ich, dass der Grund für das unterschiedliche Verhalten nur eine andere Version des Frameworks und / oder Compilers ist, die unterschiedliche Optimierungen / Berechnungen für konstante Ausdrücke verwenden, aber in anderen Fällen ist das Verhalten sehr gleich.

Dropoutcoder
quelle
7.0 ist ein Double, kein Float. Versuchen Sie 7.0f, es gibt Ihnen immer noch eine 0
Jalsh
Ja, es sollte ein Gleitkommatyp sein, kein Gleitkomma. Danke für die Korrektur.
Dropoutcoder
Dies ändert die gesamte Perspektive des Problems. Wenn Sie sich mit einer doppelten Genauigkeit befassen, ist die Genauigkeit, die Sie erhalten, viel höher, und das in der Antwort von V0ldek erläuterte Ergebnis ändert sich drastisch. Sie können die Skalierung lieber einfach auf doppelt ändern und erneut prüfen, ob die Ergebnisse gleich sind. ..
Jalsh
Am Ende ist es ein komplexeres Thema.
Dropoutcoder
1
@jalsh Ja, aber es gibt ein Compiler-Flag, das den überprüften Kontext überall umdreht. Möglicherweise möchten Sie alles auf Sicherheit prüfen lassen, mit Ausnahme eines bestimmten Hot Paths, der alle CPU-Zyklen benötigt, die er erhalten kann.
V0ldek