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

问题描述

我观察到一些非常奇怪的事情.如果你在 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.很明显 ab 大 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?

推荐答案

DoubleFloat 的尾数中没有足够的位来准确表示 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 值.问题变成了,在导致不同的 DoubleFloat 值之前,您需要减少多少 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 Ints 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)) 给我一个错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-26 04:17