C# 将浮点数作为整数进行比较会导致OverflowException,调试会显示有趣的位值

C# 将浮点数作为整数进行比较会导致OverflowException,调试会显示有趣的位值,c#,floating-point,precision,C#,Floating Point,Precision,我需要为我的项目进行自定义浮点比较,最后使用了此处提供的方法: 虽然知道float.GetHashCode()会像不安全的代码反引用变体一样返回位,但我跳过了位转换和反引用,而是使用GetHashCode() 在发现我遇到的问题后,我测试了BitConverter是否会提供不同的值,但事实并非如此 该问题出现在两个不同的地方,并且由于比较相同的值而巧合地出现:1f和-4f。这些值引起了我的好奇心,当调用Math.Abs(a-b)命中OverflowException(a-b等于Int32.Min

我需要为我的项目进行自定义浮点比较,最后使用了此处提供的方法:
虽然知道
float.GetHashCode()
会像不安全的代码反引用变体一样返回位,但我跳过了位转换和反引用,而是使用
GetHashCode()

在发现我遇到的问题后,我测试了BitConverter是否会提供不同的值,但事实并非如此

该问题出现在两个不同的地方,并且由于比较相同的值而巧合地出现:
1f
-4f
。这些值引起了我的好奇心,当调用
Math.Abs(a-b)
命中
OverflowException
a-b
等于
Int32.MinValue
)时,我继续调试这个案例

当我发现
1f
-4f
的绝对哈希码(位值)是相同的时,我非常惊讶,这最终会把数学搞砸

我接着进行了更多的调试(还考虑了一些),提供了不同的值(
float
->
GetHashCode()
),并看到了有趣的结果:

有趣的部分是假设
2f
-2f
具有相同的绝对位值

  • 为什么
    1f
    4f
    对存在差异?
  • 为什么
    1f
    4f
    对有点反向相关?
  • 我打赌我的推理完全不合逻辑,但我只是好奇

    是的,
    1f
    -4f
    之间的关系扼杀了我的比较,因为:

    int aBits = a.GetHashCode(); // -4 => -1065353216
    if (aBits < 0)
        aBits = Int32.MinValue - aBits; // -1082130432
    
    int bBits = b.GetHashCode(); // 1 => 1065353216
    if (bBits < 0)
        bBits = Int32.MinValue - bBits; // no changes, 1065353216
    
    int bitDifference = aBits - bBits; // -1082130432 - 1065353216 = -2147483648 = Int32.MinValue
    int absoluteBitDifference = Math.Abs(bitDifference); // an obvious OverflowException
    
    int-aBits=a.GetHashCode();//-4 => -1065353216
    if(aBits<0)
    aBits=Int32.MinValue-aBits;//-1082130432
    int bBits=b.GetHashCode();//1 => 1065353216
    if(bBits<0)
    bBits=Int32.MinValue-bBits;//无变化,10653216
    int bitDifference=aBits-bBits;//-1082130432-1065353216=-2147483648=Int32.MinValue
    int absoluteBitDifference=Math.Abs(位差);//明显的越位
    
  • 如何预防此问题?

  • 为了让那些没有关注链接的人受益,您关注链接()的回复中的文章以这条严厉的警告开头(突出显示在粗体中的内容是我的):

    这篇文章已经过时。它的替代品——它将修复一些错误并更好地解释相关问题——正在作为一个由多部分组成的系列进行编写。请更新您的链接

    一旦本系列文章完成,这篇文章最终将消失

    我是认真的此代码的一些问题包括别名问题、整数溢出,以及试图进一步扩展基于ULPs的技术的尝试。。上面列出的一系列文章涵盖了整个主题,但是可以找到展示良好的浮点比较技术的关键文章。本文还包括一个很酷的演示,使用sin(double(pi)),说明ULPs技术和其他相对误差技术为什么会在零附近崩溃

    总之,停止阅读。单击

    如果您仔细阅读并查看相关浮点数的位模式,您的问题
    1.
    2.
    将得到回答(如果您以二进制或十六进制打印它们,而不是十进制打印它们,则更容易)

    问题
    3.
    “如何防止此问题?”只能用“停止摆弄浮点位表示,成为IEEE-754格式的专家”来回答

    最有可能的是,有其他安全和标准的方法来完成你试图完成的任何事情,但这只是猜测,因为你没有提到练习的最终目的。

    答案已经给出。您要么需要将浮点数视为黑匣子,甚至不尝试查看它们的位模式,要么需要了解位模式的含义。仅仅看它们的十进制值是很难重建的

    问题1和2中成对位模式的十六进制值为:

    1f: 3f800000
    -1f: bf800000
    4f: 40800000
    -4f: c0800000
    
    相反的行为是由于负数处理的基本差异而产生的。IEEE二进制浮点是一种符号和幅度系统,因此-1f仅为1f,最高有效位从0更改为1。典型的整数表示法使用2的补码,因此要得到一个数字的反数,可以从0x100000000中减去它。非常大的幅值负浮点的表示形式类似于非常小的幅值负整数


    1f和-4f的位模式是彼此的补码,而-1f和4f的位模式也是如此。

    很难回答第3部分的问题,因为很难看出问题所在,因为您没有解释“自定义浮点比较”的作用。这个标题,哦,天哪。。。RTFM!
    1f: 3f800000
    -1f: bf800000
    4f: 40800000
    -4f: c0800000