Nig*_*888 14 .net c# binaryfiles
我一直在疯狂地尝试读取使用Java程序编写的二进制文件(我将Java库移植到C#并希望保持与Java版本的兼容性).
该组件的作者选择使用float乘法和乘法来确定一段数据的开始/结束偏移.不幸的是,它在.NET中的工作方式与Java相比有所不同.在Java中,库使用Float.intBitsToFloat(someInt)其中的价值someInt是1080001175.
int someInt = 1080001175;
float result = Float.intBitsToFloat(someInt);
// result (as viewed in Eclipse): 3.4923456
Run Code Online (Sandbox Code Playgroud)
之后,此数字乘以一个值以确定开始和结束位置.在这种情况下,索引值为时会出现问题2025.
int idx = 2025;
long result2 = (long)(idx * result);
// result2: 7072
Run Code Online (Sandbox Code Playgroud)
根据我的计算器,这个计算的结果应该是7071.99984.但是在Java中它恰好 7072在它被投入很长时间之前,在这种情况下它仍然存在7072.为了使因子准确 7072,浮动的值必须是3.492345679012346.
3.492345679012346代替3.4923456(Eclipse中显示的值)是否安全?现在,我正在寻找一种在.NET中获得完全相同结果的方法.但到目前为止,我只能使用hack读取这个文件,而且我并不完全确定hack适用于Java中的库生成的任何文件.
根据Java VS C#中的intBitsToFloat方法?,等效功能使用:
int someInt = 1080001175;
int result = BitConverter.ToSingle(BitConverter.GetBytes(someInt), 0);
// result: 3.49234557
Run Code Online (Sandbox Code Playgroud)
这使得计算:
int idx = 2025;
long result2 = (long)(idx * result);
// result2: 7071
Run Code Online (Sandbox Code Playgroud)
在转换为long之前的结果是7071.99977925,这与7072Java产生的价值相悖.
从那里,我认为必须有之间的数学差一些Float.intBitsToFloat(someInt),并BitConverter.ToSingle(BitConverter.GetBytes(value), 0)接收这些不同的结果.所以,我查询了intBitsToFloat(int)的javadoc,看看我是否可以在.NET中重现Java结果.我结束了:
public static float Int32BitsToSingle(int value)
{
if (value == 0x7f800000)
{
return float.PositiveInfinity;
}
else if ((uint)value == 0xff800000)
{
return float.NegativeInfinity;
}
else if ((value >= 0x7f800001 && value <= 0x7fffffff) || ((uint)value >= 0xff800001 && (uint)value <= 0xffffffff))
{
return float.NaN;
}
int bits = value;
int s = ((bits >> 31) == 0) ? 1 : -1;
int e = ((bits >> 23) & 0xff);
int m = (e == 0) ? (bits & 0x7fffff) >> 1 : (bits & 0x7fffff) | 0x800000;
//double r = (s * m * Math.Pow(2, e - 150));
// value of r: 3.4923455715179443
float result = (float)(s * m * Math.Pow(2, e - 150));
// value of result: 3.49234557
return result;
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,结果与使用时完全相同BitConverter,并且在转换为float数字之前,该数字3.4923455715179443要比3.492345679012346结果准确所需的()的假定Java值低得多()7072.
我尝试了这个解决方案,但结果值完全相同,3.49234557.
我也尝试过舍入和截断,但当然这使得所有其他值都不是非常接近整数就错了.
我可以通过在浮点值在整数的某个范围内时更改计算来破解这一点,但是因为可能有其他地方计算非常接近整数,这个解决方案可能不起作用普遍.
float avg = (idx * averages[block]);
avgValue = (long)avg; // yields 7071
if ((avgValue + 1) - avg < 0.0001)
{
avgValue = Convert.ToInt64(avg); // yields 7072
}
Run Code Online (Sandbox Code Playgroud)
请注意,该Convert.ToInt64函数在大多数情况下也不起作用,但在这种特殊情况下它具有舍入效果.
如何在.NET中创建一个返回与Java 完全相同的结果的函数Float.intBitsToFloat(int)?或者,我怎样才能将浮点计算中的差异归一化,因此这个结果是7072(不7071)给定值1080001175和2025?
注意:对于所有其他可能的整数值,它应该与Java相同.以上情况只是.NET中可能计算不同的许多地方之一.
我正在使用.NET Framework 4.5.1和.NET Standard 1.5,它应该在两个环境
x86和x64环境中产生相同的结果.
C#和Java(以及任何其他体面的编程平台)中4字节浮点数的定义基于IEEE标准,因此二进制格式是相同的.
所以,它应该工作.事实上它确实有效,但仅适用于X64目标(我之前关于.NET 2和4的评论可能是错误或正确的,我无法真正测试旧的平台二进制文件).
如果您希望它适用于所有目标,您必须像这样定义它:
long result2 = (long)(float)(idx * result);
Run Code Online (Sandbox Code Playgroud)
如果查看生成的IL,它会在乘法后添加一个补充的conv.r4操作码.我想这会在编译的x86代码中强制浮点数实现.我想这是一个jit优化问题.
我对jit优化知之甚少,以确定它是否是一个bug.有趣的是Visual Studio 2017 IDE甚至会将演员(float)文本和报告视为"多余"或"不必要",因此它闻起来不太好.