用户登录
用户注册

分享至

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

  • 作者: 哦你看太天
  • 来源: 51数据库
  • 2022-10-25

问题描述

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

fatal error: Float value cannot be converted to Int because the result would be greater than 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 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,怎么办能代表比它大一的数字吗?

If float does not have enough bits to represent Int.max, how is it able to represent a number one larger than that?

浮点数表示为两部分:尾数和指数.尾数表示有效数字(二进制??),指数表示 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 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.

请参阅@MartinR 的回答,了解 Java 正在做什么.

See @MartinR's answer for an explanation of what Java is doing.

软件
前端设计
程序设计
Java相关