为什么Int(Float(Int.max))给我一个错误? [英] Why does Int(Float(Int.max)) give me an error?

查看:192
本文介绍了为什么Int(Float(Int.max))给我一个错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我观察到了一些非常奇怪的东西.如果您在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.很明显,ab大了36854775807.我也了解到浮点数是不准确的,所以我期望小于Int.max的东西,最后几位是0.

我也用Double尝试过,它也崩溃了.

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

long a = Long.MAX_VALUE;
float b = (float)a;
long c = (long)b;
System.out.println(c);

它会打印出预期的9223372036854754775807!

迅速有什么问题吗?

解决方案

DoubleFloat的尾数没有足够的位数来准确表示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.max1.


许多值将转换为相同的Float值.问题是,要减少Int.maxFloat的值,您必须减少多少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 Ints 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆