为什么Int(Float(Int.max))给我一个错误? [英] Why does Int(Float(Int.max)) give me an error?
问题描述
我观察到了一些非常奇怪的东西.如果您在Swift中运行以下代码:
Int(Float(Int.max))
它崩溃并显示错误消息:
致命错误:浮点值不能转换为Int,因为结果将大于Int.max
这确实是违反直觉的,所以我将表达式扩展为3行,并尝试查看在操场上每一步会发生什么:
let a = Int.max
let b = Float(a)
let c = Int(b)
它崩溃并显示相同的消息.这次,我看到a
是9223372036854775807,而b
是9.223372e + 18.很明显,a
比b
大了36854775807.我也了解到浮点数是不准确的,所以我期望小于Int.max
的东西,最后几位是0.
我也用Double
尝试过,它也崩溃了.
然后我想,也许这就是浮点数的行为,所以我在Java中测试了同样的东西:
long a = Long.MAX_VALUE;
float b = (float)a;
long c = (long)b;
System.out.println(c);
它会打印出预期的9223372036854754775807!
迅速有什么问题吗?
Double
或Float
的尾数没有足够的位数来准确表示19
有效数字,因此您需要四舍五入结果.
如果使用String(format:)
打印Float
,则可以看到Float
值的更准确表示:
let a = Int.max
print(a) // 9223372036854775807
let b = Float(a)
print(String(format: "%.1f", b)) // 9223372036854775808.0
因此Float
表示的值比Int.max
大1
.
许多值将转换为相同的Float
值.问题是,要减少Int.max
或Float
的值,您必须减少多少Int.max
.
以Double
开头:
var y = Int.max
while Double(y) == Double(Int.max) {
y -= 1
}
print(Int.max - y) // 512
因此,对于Double
,最后一个512
Int
都将转换为相同的Double
.
Float
具有较少的比特来表示该值,因此有更多的值都映射到相同的Float
.切换到- 1000
,使其在合理的时间运行:
var y = Int.max
while Float(y) == Float(Int.max) {
y -= 1000
}
print(Int.max - y) // 274877907000
因此,您对Float
可以准确表示特定Int
的期望被放错了位置.
关注评论中的问题:
如果float没有足够的位来表示Int.max,怎么办 能够代表比那大一的数字?
浮点数表示为两部分:尾数和指数.尾数表示有效数字(二进制),指数表示2的幂.结果,浮点数可以通过使尾数为1并用代表幂的指数来精确地表示2的偶数幂. /p>
不是2的幂的数字可能具有二进制模式,该二进制模式包含的数字比尾数表示的位数还多. Int.max
(2 ^ 63-1)就是这种情况,因为在二进制文件中,111111111111111111111111111111111111111111111111111111111111111
(63 1). 32位的Float
不能存储63位的尾数,因此必须四舍五入.对于Int.max
,将其舍入为1将得出该值
1000000000000000000000000000000000000000000000000000000000000000
.从左边开始,尾数仅表示1个有效位(尾随的0
是免费提供的),因此该数字是尾数1
和指数64
.>
有关Java在做什么的解释,请参见@MartinR的答案.
I observed something really strange. If you run this code in Swift:
Int(Float(Int.max))
It crashes with the error message:
fatal error: Float value cannot be converted to Int because the result would be greater than Int.max
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)
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.
I also tried this with Double
, it crashes too.
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);
It prints the expected 9223372036854775807!
What is wrong with swift?
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.
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
So the value represented by the Float
is 1
larger than 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.
Starting with Double
:
var y = Int.max
while Double(y) == Double(Int.max) {
y -= 1
}
print(Int.max - y) // 512
So with Double
, the last 512
Int
s all convert to the same Double
.
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
So, your expectation that a Float
could accurately represent a specific Int
was misplaced.
Follow up question from the comments:
If float does not have enough bits to represent Int.max, how is it able to represent a number one larger than that?
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.
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 value
1000000000000000000000000000000000000000000000000000000000000000
. 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
.
See @MartinR's answer for an explanation of what Java is doing.
这篇关于为什么Int(Float(Int.max))给我一个错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!