C# 将浮点值从二进制形式转换为NaN值,反之亦然,会导致不匹配
我在没有任何算术运算的情况下进行“字节[4]->浮点数->字节[4]”转换。 以字节为单位,我有一个IEEE-754格式的单精度数字(每个数字4字节,与机器中的小尾端顺序相同)。 我遇到了一个问题,字节表示非逐字转换的NaN值。 例如: {0x1B,0xC4,0xAB,0x7F}->NaN->{0x1B,0xC4,0xEB,0x7F} 复制代码:C# 将浮点值从二进制形式转换为NaN值,反之亦然,会导致不匹配,c#,optimization,floating-point,jit,C#,Optimization,Floating Point,Jit,我在没有任何算术运算的情况下进行“字节[4]->浮点数->字节[4]”转换。 以字节为单位,我有一个IEEE-754格式的单精度数字(每个数字4字节,与机器中的小尾端顺序相同)。 我遇到了一个问题,字节表示非逐字转换的NaN值。 例如: {0x1B,0xC4,0xAB,0x7F}->NaN->{0x1B,0xC4,0xEB,0x7F} 复制代码: using System; using System.Linq; namespace StrangeFloat { class Progra
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);
}
}
}
结果():
此行为取决于平台和配置:此代码在所有配置或x86/Debug中的x64上转换一个没有错误的数字。在x86/Release上存在错误
还有,如果我改变
byte[] bitConverterResult = BitConverter.GetBytes(array[0]);
到
然后它也会在x86/Debug上出错
我确实研究了这个问题,发现编译器生成的x86代码使用FPU寄存器(!)来保存浮点值(FLD/FST指令)。但FPU将尾数的高位设置为1而不是0,所以它修改了值,尽管逻辑只是传递一个值而没有更改。
在x64平台上,使用了一个xmm0寄存器(SSE),并且工作正常
[问题]
这是什么:对于NaN值或JIT/优化错误,这是一个未定义的行为
为什么编译器在没有进行算术运算的情况下使用FPU和SSE
更新1
调试配置-通过堆栈传递值,无副作用-正确结果:
发布配置-优化器或JIT通过FPU寄存器执行奇怪的传递并中断数据-不正确
我只是把@HansPassant的评论翻译成一个答案
“x86抖动使用FPU处理浮点值。这是 不是bug。你假设那些字节值是正确的 使用浮点参数的方法的参数是错误的。”
换句话说,这只是一个GIGO案例(垃圾输入,垃圾输出)。在IEEE规范中,有多个值对
NaN
有效。调试和发布是否得到相同的结果?我相信调试是用软件来模拟FPU,而发行版是在计算机上使用FPU。这台电脑有多旧?我相信某些上浮点单元存在一些已知问题。英特尔处理器手册:“如果源操作数中的一个或两个都是NaN,并且浮点无效操作异常被屏蔽,结果如表4-7所示。当SNaN转换为QNaN时,通过将SNaN的最高有效分数位设置为1来处理转换。此外,当其中一个源操作数是SNaN时,它将设置浮点无效操作异常标志。请注意,对于源操作数的某些组合,x87 FPU操作和SSE/SSE2/SSE3/SSE4.1操作的结果是不同的。英特尔AVX遵循与SSE/SSE2相同的行为…@jdweng我在调试和发布时得到不同的结果,请参阅更新后:在调试模式下,数据通过堆栈传递,这是正常的,但在发布模式下,数据通过FPU泵送-为什么优化器/JIT会这样做?(CPU Core2 Quad Q9550,这不是硬件问题)x86抖动使用FPU来处理浮点值。这不是错误。您认为这些字节值是接受浮点参数的方法的正确参数的假设是错误的。信号NaN的唯一用途是生成异常。NET Framework不会。并且.NET代码cou不会执行浮点操作你需要调查这些字节值的来源,它有一个bug。
byte[] bitConverterResult = BitConverter.GetBytes(array[0]);
float f = array[0];
byte[] bitConverterResult = BitConverter.GetBytes(f);
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
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]