Ich mache eine Konvertierung "Bytes [4] -> Gleitzahl -> Bytes [4]" ohne jede Arithmetik. In Bytes habe ich eine Nummer mit einfacher Genauigkeit im IEEE-754-Format (4 Bytes pro Nummer, Little-Endian-Reihenfolge wie in einer Maschine). Ich stoße auf ein Problem, wenn Bytes einen nicht wörtlich konvertierten NaN-Wert darstellen. Zum Beispiel:Konvertieren Float NaN-Werte von Binärform und umgekehrt führt zu einer Nichtübereinstimmung
{0x1B, 0xC4, 0xAB, 0x7F} -> NaN -> {0x1B, 0xC4, 0xEB, 0x7F}
-Code für die Wiedergabe:
using System;
using System.Linq;
namespace StrangeFloat
{
class Program
{
private static void PrintBytes(byte[] array)
{
foreach (byte b in array)
{
Console.Write("{0:X2}", b);
}
Console.WriteLine();
}
static void Main(string[] args)
{
byte[] strangeFloat = { 0x1B, 0xC4, 0xAB, 0x7F };
float[] array = new float[1];
Buffer.BlockCopy(strangeFloat, 0, array, 0, 4);
byte[] bitConverterResult = BitConverter.GetBytes(array[0]);
PrintBytes(strangeFloat);
PrintBytes(bitConverterResult);
bool isEqual = strangeFloat.SequenceEqual(bitConverterResult);
Console.WriteLine("IsEqual: {0}", isEqual);
}
}
}
Ergebnis (https://ideone.com/p5fsrE):
1BC4AB7F
1BC4EB7F
IsEqual: False
Dieses Verhalten hängt Von Plattform und Konfiguration: Dieser Code konvertiert eine Nummer ohne Fehler auf x64 in allen Konfigurationen oder in x86/Debug. Auf x86/Release ist ein Fehler aufgetreten.
Auch, wenn ich
ändernbyte[] bitConverterResult = BitConverter.GetBytes(array[0]);
zu
float f = array[0];
byte[] bitConverterResult = BitConverter.GetBytes(f);
dann erroneus auch auf x86/Debug.
ich tun um das Problem zu erforschen und festgestellt, dass Compiler x86-Code, der eine FPU Register verwenden (!) Zu einem halten einen Float-Wert (FLD/FST Anweisungen) erzeugen. Aber FPU setzt ein hohes Mantissen-Bit auf 1 statt auf 0, also ändert es den Wert, obwohl die Logik nur einen Wert ohne Änderung übergibt. Auf x64-Plattform ein xmm0 registrieren verwendet (SSE) und es funktioniert gut.
[Frage]
Was ist das: es ist ein irgendwo nicht definiertes Verhalten für einen NaN-Werte oder eineJIT/Optimierung Fehler dokumentiert?
Warum Compiler eine FPU und SSE verwenden, wenn keine arithmetischen Operationen vorgenommen wurden?
aktualisiert 1
Debug Konfiguration - Pass Wert über Stapel ohne Nebenwirkungen - korrektes Ergebnis:
byte[] bitConverterResult = BitConverter.GetBytes(array[0]);
02232E45 mov eax,dword ptr [ebp-44h]
02232E48 cmp dword ptr [eax+4],0
02232E4C ja 02232E53
02232E4E call 71EAC65A
02232E53 push dword ptr [eax+8] // eax+8 points to "1b c4 ab 7f" CORRECT!
02232E56 call 7136D8E4
02232E5B mov dword ptr [ebp-5Ch],eax // eax points to managed
// array data "fc 35 d7 70 04 00 00 00 __1b c4 ab 7f__" and this is correct
02232E5E mov eax,dword ptr [ebp-5Ch]
02232E61 mov dword ptr [ebp-48h],eax
Veröffentlichung Konfiguration - Optimierer oder ein JIT tut einen seltsamen Pass über FPU registriert und bricht eine Daten - inkorrekt
byte[] bitConverterResult = BitConverter.GetBytes(array[0]);
00B12DE8 cmp dword ptr [edi+4],0
00B12DEC jbe 00B12E3B
00B12DEE fld dword ptr [edi+8] // edi+8 points to "1b c4 ab 7f"
00B12DF1 fstp dword ptr [ebp-10h] // ebp-10h points to "1b c4 eb 7f" (FAIL)
00B12DF4 mov ecx,dword ptr [ebp-10h]
00B12DF7 call 70C75810
00B12DFC mov edi,eax
00B12DFE mov ecx,esi
00B12E00 call dword ptr ds:[4A70860h]
Es gibt mehrere Werte, die in der IEEE-Spezifikation für 'NaN' gelten. – leppie
Erhalten Sie das gleiche Ergebnis mit Debug und Release? Ich glaube, dass Debug verwendet Software FPU zu simulieren, während Freigabe FPU in Computer verwenden. Wie alt ist der PC? Ich glaube, es gibt bekannte Probleme mit einigen UP-Gleitkomma-Einheiten. – jdweng
Intel-Prozessorhandbuch: "Wenn einer oder beide Quelloperanden NaNs sind und die Gleitkommaoperation ungültig ist, wird die Operation maskiert, das Ergebnis ist in Tabelle 4-7 dargestellt. Wenn ein SNaN in ein QNaN konvertiert wird, wird die Konvertierung durchgeführt wird gehandhabt, indem ** das höchstwertige Bit des SNaN auf 1 gesetzt wird ** Auch wenn einer der Quelloperanden ein SNaN ist, wird das Flag für den ungültigen Betrieb des Gleitkommawerts gesetzt Es ist zu beachten, dass für einige Kombinationen von Quelloperanden, das Ergebnis ist unterschiedlich für x87 FPU Operationen und für SSE/SSE2/SSE3/SSE4.1 Operationen. Intel AVX folgt dem gleichen Verhalten als SSE/SSE2 ... " –