将浮点数作为整数进行比较会导致 OverflowException,调试会显示有趣的位值
Comparing floats as integers causes OverflowException, debugging reveals interesting bit values
我需要为我的项目进行自定义浮动比较,最后我使用了此处提供的方法:
虽然,知道 float.GetHashCode()
returns 位以及代码的不安全取消引用变体,我跳过了位转换和取消引用,而是使用 GetHashCode()
。
在发现我遇到的问题后,我测试了 BitConverter 是否会提供不同的值,但事实并非如此。
问题出现在两个不同的地方,巧合的是由于比较相同的值,分别是:1f
和-4f
。这些值让我很好奇,当对 Math.Abs(a - b)
的调用命中 OverflowException
(a - b
等于 Int32.MinValue
)时,我继续调试案例。
当我发现 1f
和 -4f
的绝对哈希码(位值)相同时,我感到非常惊讶,这最终搞乱了数学。
我继续调试了一点(也考虑了一些flaws provided here),提供不同的值(float
->
GetHashCode()
)并见证有趣 结果:
0f -> 0
-0f -> -2147483648
float.MinValue -> -8388609
float.MaxValue -> 2139095039
1f -> 1065353216
-1f -> -1082130432
2f -> 1073741824
-2f -> -1073741824
4f -> 1082130432
-4f -> -1065353216
有趣的部分是...假定 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
- 如何防止这个问题?
为了那些没有点击链接的人的利益,您点击的回复链接到的文章 (Comparing floating point numbers) 以这个严厉的警告开头( 粗体 是我的):
This article is obsolete. Its replacement - which will fix some errors and better explain the relevant issues - is being crafted as a multi-part series here. Please update your links.
Ultimately this article will go away, once the series of articles is complete.
I mean it. Some of the problems with this code include aliasing problems, integer overflow, and an attempt to extend the ULPs based technique further than really makes sense. The series of articles listed above covers the whole topic, but the key article that demonstrates good techniques for floating-point comparisons can be found here. This article also includes a cool demonstration, using sin(double(pi)), of why the ULPs technique and other relative error techniques breaks down around zero.
In short, stop reading. Click this link.
如果您阅读 IEEE floating point 并查看相关浮点数的位模式,您的问题 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 也类似。
我需要为我的项目进行自定义浮动比较,最后我使用了此处提供的方法:
虽然,知道 float.GetHashCode()
returns 位以及代码的不安全取消引用变体,我跳过了位转换和取消引用,而是使用 GetHashCode()
。
在发现我遇到的问题后,我测试了 BitConverter 是否会提供不同的值,但事实并非如此。
问题出现在两个不同的地方,巧合的是由于比较相同的值,分别是:1f
和-4f
。这些值让我很好奇,当对 Math.Abs(a - b)
的调用命中 OverflowException
(a - b
等于 Int32.MinValue
)时,我继续调试案例。
当我发现 1f
和 -4f
的绝对哈希码(位值)相同时,我感到非常惊讶,这最终搞乱了数学。
我继续调试了一点(也考虑了一些flaws provided here),提供不同的值(float
->
GetHashCode()
)并见证有趣 结果:
0f -> 0
-0f -> -2147483648
float.MinValue -> -8388609
float.MaxValue -> 2139095039
1f -> 1065353216
-1f -> -1082130432
2f -> 1073741824
-2f -> -1073741824
4f -> 1082130432
-4f -> -1065353216
有趣的部分是...假定 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
- 如何防止这个问题?
为了那些没有点击链接的人的利益,您点击的回复链接到的文章 (Comparing floating point numbers) 以这个严厉的警告开头( 粗体 是我的):
This article is obsolete. Its replacement - which will fix some errors and better explain the relevant issues - is being crafted as a multi-part series here. Please update your links.
Ultimately this article will go away, once the series of articles is complete.
I mean it. Some of the problems with this code include aliasing problems, integer overflow, and an attempt to extend the ULPs based technique further than really makes sense. The series of articles listed above covers the whole topic, but the key article that demonstrates good techniques for floating-point comparisons can be found here. This article also includes a cool demonstration, using sin(double(pi)), of why the ULPs technique and other relative error techniques breaks down around zero.
In short, stop reading. Click this link.
如果您阅读 IEEE floating point 并查看相关浮点数的位模式,您的问题 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 也类似。