问题描述
我观察到一些非常奇怪的事情.如果你在 Swift 中运行这段代码:
I observed something really strange. If you run this code in Swift:
Int(Float(Int.max))
它崩溃并显示错误消息:
It crashes with the error message:
致命错误:浮点值无法转换为 Int,因为结果会大于 Int.max
这真的很违反直觉,所以我将表达式扩展为 3 行,并尝试查看操场中的每个步骤会发生什么:
This is really counter-intuitive, so I expanded the expression into 3 lines and tried to see what happens in each step in a playground:
let a = Int.max
let b = Float(a)
let c = Int(b)
它崩溃并显示相同的消息.这一次,我看到 a
是 9223372036854775807 而 b
是 9.223372e+18.很明显 a
比 b
大 36854775807.我也知道浮点数是不准确的,所以我期望小于 Int.max
,最后几位为0.
It crashes with the same message. This time, I see that a
is 9223372036854775807 and b
is 9.223372e+18. It is obvious that a
is greater than b
by 36854775807. I also understand that floating points are inaccurate, so I expected something less than Int.max
, with the last few digits being 0.
我也用 Double
试过这个,它也崩溃了.
I also tried this with Double
, it crashes too.
然后我想,也许这就是浮点数的行为方式,所以我在 Java 中测试了同样的东西:
Then I thought, maybe this is just how floating point numbers behave, so I tested the same thing in Java:
long a = Long.MAX_VALUE;
float b = (float)a;
long c = (long)b;
System.out.println(c);
它打印出预期的 9223372036854775807!
It prints the expected 9223372036854775807!
swift 有什么问题?
What is wrong with swift?
推荐答案
Double
或 Float
的尾数中没有足够的位来准确表示 19
位有效数字,因此您得到的是四舍五入的结果.
There aren't enough bits in the mantissa of a Double
or Float
to accurately represent 19
significant digits, so you are getting a rounded result.
如果您使用 String(format:)
打印 Float
,您可以看到 Float
值的更准确表示:
If you print the Float
using String(format:)
you can see a more accurate representation of the value of the Float
:
let a = Int.max
print(a) // 9223372036854775807
let b = Float(a)
print(String(format: "%.1f", b)) // 9223372036854775808.0
所以Float
所代表的值是1
大于Int.max
.
So the value represented by the Float
is 1
larger than Int.max
.
许多值将被转换为相同的 Float
值.问题变成了,在导致不同的 Double
或 Float
值之前,您需要减少多少 Int.max
.
Many values will be converted to the same Float
value. The question becomes, how much would you have to reduce Int.max
before it results in a different Double
or Float
value.
从Double
开始:
var y = Int.max
while Double(y) == Double(Int.max) {
y -= 1
}
print(Int.max - y) // 512
所以使用 Double
,最后的 512
Int
都转换为相同的 Double
.
So with Double
, the last 512
Int
s all convert to the same Double
.
Float
有更少的位来表示值,因此有更多的值都映射到相同的 Float
.切换到 - 1000
以便它在合理的时间内运行:
Float
has fewer bits to represent the value, so there are more values that all map to the same Float
. Switching to - 1000
so that it runs in reasonable time:
var y = Int.max
while Float(y) == Float(Int.max) {
y -= 1000
}
print(Int.max - y) // 274877907000
因此,您对 Float
可以准确表示特定 Int
的期望是错误的.
So, your expectation that a Float
could accurately represent a specific Int
was misplaced.
从评论中跟进问题:
如果float没有足够的位来表示Int.max,怎么办能代表比它大一的数字吗?
浮点数表示为两部分:尾数和指数.尾数表示有效数字(二进制),指数表示 2 的幂.因此,浮点数可以准确地表示 2 的偶数幂,方法是尾数为 1,指数表示幂.
Floating point numbers are represented as two parts: mantissa and exponent. The mantissa represents the significant digits (in binary) and the exponent represents the power of 2. As a result, a floating point number can accurately express an even power of 2 by having a mantissa of 1 with an exponent that represents the power.
甚至不是 2 的幂的数字可能具有二进制模式,其中包含的数字多于尾数中可以表示的数字.Int.max
(即 2^63 - 1)就是这种情况,因为在二进制中是 111111111111111111111111111111111111111111111111111111111111111
(63 个 1).32 位的 Float
不能存储 63 位的尾数,因此必须对其进行舍入或截断.在 Int.max
的情况下,向上舍入 1 会得到值10000000000000000000000000000000000000000000000000000000000000000
.从左边开始,尾数只有1个有效位(后面的0
是免费的),所以这个数是1
的尾数和 64
的指数.
Numbers that are not even powers of 2 may have a binary pattern that contains more digits than can be represented in the mantissa. This is the case for Int.max
(which is 2^63 - 1) because in binary that is 111111111111111111111111111111111111111111111111111111111111111
(63 1's). A Float
which is 32 bits cannot store a mantissa which is 63 bits, so it has to be rounded or truncated. In the case of Int.max
, rounding up by 1 results in the value1000000000000000000000000000000000000000000000000000000000000000
. Starting from the left, there is only 1 significant bit to be represented by the mantissa (the trailing 0
's come for free), so this number is a mantissa of 1
and an exponent of 64
.
请参阅@MartinR 的回答,了解 Java 正在做什么.
See @MartinR's answer for an explanation of what Java is doing.
这篇关于为什么 Int(Float(Int.max)) 给我一个错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!