为什么Int(Float(Int.max))给我一个错误?

Swe*_*per 5 floating-point type-conversion swift

我发现一些非常奇怪的东西 如果您在Swift中运行此代码:

Int(Float(Int.max))
Run Code Online (Sandbox Code Playgroud)

它与错误消息崩溃:

致命错误:浮点值无法转换为Int,因为结果将大于Int.max

这实际上是违反直觉的,所以我将表达式扩展为3行,并试图了解操场中每个步骤会发生什么:

let a = Int.max
let b = Float(a)
let c = Int(b)
Run Code Online (Sandbox Code Playgroud)

它崩溃了相同的消息.这一次,我看到的a是9223372036854775807,b是9.223372e + 18.很明显,a大于b36854775807.我也明白浮点数是不准确的,所以我期望小于Int.max,最后几位为0.

我也试过这个Double,它也崩溃了.

然后我想,也许这就是浮点数的行为,所以我在Java中测试了同样的东西:

long a = Long.MAX_VALUE;
float b = (float)a;
long c = (long)b;
System.out.println(c);
Run Code Online (Sandbox Code Playgroud)

它打印预期9223372036854775807!

swift出了什么问题?

vac*_*ama 7

a的尾数中没有足够的位DoubleFloat准确表示19有效数字,因此您得到一个舍入结果.

如果您打印Float使用,String(format:)您可以看到更准确的值的表示Float:

let a = Int.max
print(a)                          // 9223372036854775807
let b = Float(a)
print(String(format: "%.1f", b))  // 9223372036854775808.0
Run Code Online (Sandbox Code Playgroud)

所以由所表示的值Float1大于Int.max.


许多值将转换为相同的Float值.问题是,Int.max在产生不同DoubleFloat价值之前,你需要减少多少.

从以下开始Double:

var y = Int.max

while Double(y) == Double(Int.max) {
    y -= 1
}

print(Int.max - y)  // 512
Run Code Online (Sandbox Code Playgroud)

所以Double,最后的512 Ints都转换为相同的Double.

Float具有较少的位来表示该值,因此有更多的值都映射到相同的值Float.切换到- 1000使其在合理的时间内运行:

var y = Int.max

while Float(y) == Float(Int.max) {
    y -= 1000
}

print(Int.max - y)  // 274877907000
Run Code Online (Sandbox Code Playgroud)

因此,您对a Float能够准确表示具体情况的期望Int是错误的.


跟进评论中的问题:

如果float没有足够的位来表示Int.max,那么它如何能够代表比这更大的数字呢?

浮点数表示为两部分:尾数和指数.尾数表示有效数字(二进制​​),指数表示2的幂.结果,浮点数可以通过使尾数为1并且指数表示幂来精确地表示2的偶数幂.

不是2的幂的数字可以具有二进制模式,其包含比在尾数中可以表示的更多的数字.这是Int.max(2 ^ 63 - 1)的情况,因为二进制是111111111111111111111111111111111111111111111111111111111111111(63 1).Float32位的A 不能存储63位的尾数,因此必须进行舍入或截断.在这种情况下Int.max,向上舍入为1会产生该值 1000000000000000000000000000000000000000000000000000000000000000.从左边开始,尾数只有一个有效位(尾随0的是免费的),所以这个数字是尾数1和指数64.

有关Java正在做什么的解释,请参阅@ MartinR的答案.