int
和
float
都是 32 位大小的值。是否可以编写一对函数
int toInt(float f);
float toFloat(int n);
这样,如果
f1
和
f2
是任意
float
非 NaN 值,并且 i1 和 i2 是任意
int
值:
f1 < f2
当且仅当
toInt(f1) < toInt(f2)
f1 > f2
当且仅当
toInt(f1) > toInt(f2)
f1 == f2
当且仅当
toInt(f1) == toInt(f2)
toInt(toFloat(i1) == i1
toFloat(toInt(f1)) == f1
IEEE 浮点数 和双精度数的排列方式使您可以通过对原始二进制表示进行 无符号 比较来比较它们。从浮点到原始整数以及返回的函数是java.lang.Float.floatToIntBits和java.lang.Float.intBitsToFloat。这些函数是处理器内在函数,因此它们的成本极低。
长打和双打也是如此。这里的转换函数是java.lang.Double.doubleToLongBits 和 java.lang.Double.longBitsToDouble.
请注意,如果您想对整数使用正常的signed 比较,除了转换为整数之外,您还必须进行一些额外的转换。
此规则的唯一例外是 NaN,它无论如何都不允许全排序。
int n = Float.floatToRawIntBits(f);
float f2 = Float.intBitToFloat(n);
int n2 = Float.floatToRawIntBits(f2);
assert n == n2; // always
assert f == f2 || Float.isNaN(f);
作为
int
的原始位与原始
float
具有相同的排序顺序,但与
NaN
值不可比较的
float
值除外,具有作为
int
的值
注意:NaN
有多个值,它们彼此不等于
float
有 2^32 种可能的 int 值,所有这些值都是不同的。 然而,e 的浮点数少于 2^32; IE。 7FF0000000000001 到 7FF7FFFFFFFFFFFF 代表 NaN,
因此,您拥有的整数多于浮点数,并且无法将它们清楚地相互映射,因为
toFloat(i1)
无法为每个整数生成不同的浮点数
here和here描述的文章来解释为什么我们应该使用@Peter Lawrey描述的方法来比较整数和浮点数之间的底层位模式
nice和clean浮点数的域中。
注意:IEEE 浮点数的表示形式为sign_bit(1 bit) exponent(8 bits) sinificand(23 bits)
,在clean
情况下其值为:
(-1)<sup>sign</sup> * 2<sup>exp</sup> * significand
。事实上,23位代表实际有效数的小数部分,整数部分为1。
对于 0 < exp < 255
(对应于normal not null floats )作为无符号字节来说一切都很好,并且在该域中你有一个双射。 对于
exp == 255
,您拥有无限值
significand == 0
以及
significand != 0
的所有 NaN - 好吧,您明确排除了它们。但是对于
exp == 0
来说,仍然有一些奇怪的事情:当
significand == 0
时,你有 +0 和 -0。我不确定他们是否被认为是平等的。如果有人知道,请随时编辑帖子。但作为整数值,它们当然会有所不同。当
exp == 0
和
significand != 0
时,你会发现非规范化数字......虽然不相等,但会被转换为不为 0 的最小数字的 0。因此,如果您想要双射,请仅使用具有
0 < exp < 255<
的 正常
数字,并避免使用 NaN、无穷大、0 和非正规数,因为这些数字很奇怪。 参考资料:
public static long toLong(float v) {
long bits = Integer.toUnsignedLong(Float.floatToIntBits(v));
return Math.copySign(1, v) < 0 ? -bits : bits;
}
通过迭代每个非 nan 浮点值,应该需要不到一分钟的时间来确认这是否有效:
public static void main(String[] args) {
float current = Float.NEGATIVE_INFINITY;
while (current != Float.POSITIVE_INFINITY) {
float next = Math.nextUp(current);
long curLong = toLong(current);
long nextLong = toLong(next);
if (curLong >= nextLong) {
throw new RuntimeException(curLong + " is greater than " + nextLong
+ " representations of " + current + " and " + next);
}
current = next;
}
System.out.println("Reached " + current);
}