如何在C#中模仿Number.intBitsToFloat()?

时间:2017-05-16 06:06:31

标签: c# .net binaryfiles

我一直在疯狂地尝试读取使用Java程序编写的二进制文件(我将Java库移植到C#并希望保持与Java版本的兼容性)。

Java Library

组件的作者选择使用float和乘法来确定一段数据的开始/结束偏移。不幸的是,它在.NET中的工作方式与Java相比有所不同。在Java中,库使用Float.intBitsToFloat(someInt),其中someInt的值为1080001175

int someInt = 1080001175;
float result = Float.intBitsToFloat(someInt);
// result (as viewed in Eclipse): 3.4923456

稍后,此数字乘以一个值以确定开始和结束位置。在这种情况下,当索引值为2025时会出现问题。

int idx = 2025;
long result2 = (long)(idx * result);
// result2: 7072

根据我的计算器,此计算结果应为7071.99984。但是在Java中,它被完全 7072在被转换为long之前,在这种情况下它仍然是7072。为了使因子完全 7072,浮点数的值必须为3.492345679012346

假设float的值实际上是3.492345679012346而不是3.4923456(Eclipse中显示的值)是否安全?

.NET Equivalent

现在,我正在寻找一种在.NET中获得完全相同结果的方法。但到目前为止,我只能使用hack读取这个文件,而且我不完全确定hack适用于由Java库生成的任何文件。

根据intBitsToFloat method in Java VS C#?,等效功能正在使用:

int someInt = 1080001175;
int result = BitConverter.ToSingle(BitConverter.GetBytes(someInt), 0);
// result: 3.49234557

这使得计算:

int idx = 2025;
long result2 = (long)(idx * result);
// result2: 7071

转换为long之前的结果是7071.99977925,这与Java产生的7072值相差不了。

我尝试了什么

从那时起,我认为Float.intBitsToFloat(someInt)BitConverter.ToSingle(BitConverter.GetBytes(value), 0)之间的数学必须有一些差异才能收到这样的不同结果。所以,我咨询了javadocs for intBitsToFloat(int),看看我是否可以在.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;
}

正如您所看到的,结果完全与使用BitConverter时相同,并且在转换为float之前,数字相当低({{ 1}})而不是假定的(3.4923455715179443)Java值,结果恰好是3.492345679012346

我尝试this solution,但结果值完全相同,7072

我也尝试过舍入和截断,但当然这会使所有其他值与整数不太接近。

当浮点值在整数的某个范围内时,我能够通过改变计算来破解这一点,但由于可能存在计算非常接近整数的其他地方,这个解决方案可能会赢得& #39;普遍工作。

3.49234557

请注意,float avg = (idx * averages[block]); avgValue = (long)avg; // yields 7071 if ((avgValue + 1) - avg < 0.0001) { avgValue = Convert.ToInt64(avg); // yields 7072 } 函数在大多数情况下都不起作用,但在这种特殊情况下它具有舍入效果。

问题

如何在.NET中创建一个函数,使得与Java中的Convert.ToInt64完全相同?或者,我如何以其他方式规范浮点计算中的差异,因此给定值Float.intBitsToFloat(int)7072时,此结果为7071(不是1080001175)?

  

注意:对于所有其他可能的整数值,它应该与Java相同。以上情况只是.NET中计算不同的许多地方之一。

     

我使用的是.NET Framework 4.5.1和.NET Standard 1.5,它应该在2025x86环境中产生相同的结果。

2 个答案:

答案 0 :(得分:5)

C#和Java(以及任何其他体面的编程平台)中4字节浮点数的定义基于IEEE standards,因此二进制格式是相同的。

所以,它应该工作。事实上它确实有效,但仅适用于X64目标(我之前关于.NET 2和4的评论可能是错误或正确的,我无法真正测试旧的平台二进制文件。)

如果您希望它适用于所有目标,您必须像这样定义:

long result2 = (long)(float)(idx * result);

如果查看生成的IL,它会在乘法后添加一个补充conv.r4操作码。我想这会在编译的x86代码中强制浮点数实现。我想这是一个jit优化问题。

我对jit优化知之甚少,以确定它是否是一个bug。有趣的是,Visual Studio 2017 IDE甚至会将演员(float)文本和报告视为“冗余”或“不必要”,因此它闻起来不太好。

答案 1 :(得分:-1)

仅供参考 - 现在直接添加到 .net 中。

int myFloat = BitConverter.Int32BitsToSingle( myInt );

所以...(如问题所示)

int someInt = 1080001175;
float result = BitConverter.Int32BitsToSingle(someInt);

返回 3.49234557(与 Eclipse 几乎相同)