【问题标题】:Why does Int(Float(Int.max)) give me an error?为什么 Int(Float(Int.max)) 给我一个错误?
【发布时间】:2017-04-30 12:57:54
【问题描述】:

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

它打印出预期的 9223372036854775807!

swift 有什么问题?

【问题讨论】:

  • Java 只是在 Double/Float 到 long 转换中截断,例如(long)(1.0E100) 的计算结果也为 9223372036854775807。

标签: swift floating-point type-conversion


【解决方案1】:

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所代表的值是1大于Int.max


许多值将转换为相同的Float 值。问题变成了,在DoubleFloat 值不同之前,您需要减少多少Int.max

Double开头:

var y = Int.max

while Double(y) == Double(Int.max) {
    y -= 1
}

print(Int.max - y)  // 512

所以对于Double,最后一个512 Ints 都转换为相同的Double

Float 表示值的位更少,因此有更多的值都映射到相同的Float。切换到- 1000 使其在合理的时间内运行:

var y = Int.max

while Float(y) == Float(Int.max) {
    y -= 1000
}

print(Int.max - y)  // 274877907000

因此,您对 Float 可以准确表示特定 Int 的期望是错误的。


来自 cmets 的后续问题:

如果float没有足够的位来表示Int.max,怎么办 能代表比它大一的数字吗?

浮点数表示为两部分:尾数和指数。尾数表示有效数字(二进制​​),指数表示 2 的幂。因此,浮点数可以通过尾数为 1 和表示幂的指数准确地表示 2 的偶数幂。

甚至不是 2 的幂的数字可能具有二进制模式,其中包含的数字多于尾数中可以表示的数字。 Int.max(即 2^63 - 1)就是这种情况,因为在二进制中是 111111111111111111111111111111111111111111111111111111111111111(63 个 1)。 32 位的Float 无法存储 63 位的尾数,因此必须对其进行舍入或截断。在Int.max 的情况下,将值向上舍入 1 1000000000000000000000000000000000000000000000000000000000000000。从左边开始,尾数只有1个有效位(后面的0是免费的),所以这个数是1的尾数和64的指数。

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

【讨论】:

  • 如果float没有足够的位来表示Int.max,它怎么能表示比它大一的数字呢?
  • @Sweeper,这是一个很好的问题。我会更新我的答案。
  • @Sweeper 它可以简单地用尾数 1 和指数 63 存储它——所以 1 * 2^63。但是 2^63 - 1 (Int64.max) 的位模式是 63 个重复的 1,所以需要四舍五入才能存储(因为 Float 只有 32 位长)。
【解决方案2】:

转换“太大”的浮点数时,Swift 和 Java 的行为不同 数为整数。 Java 截断任何浮点值 大于Long.MAX_VALUE = 2^63-1:

long c = (long)(1.0E+30f);
System.out.println(c);
// 9223372036854775807

Swift 期望值在Int 的范围内,并中止 否则会出现运行时异常:

/// Creates a new instance by rounding the given floating-point value toward
/// zero.
///
/// - Parameter other: A floating-point value. When `other` is rounded toward
///   zero, the result must be within the range `Int.min...Int.max`.
public init(_ value: Float)

例子:

let c = Int(Float(1.0E30))
print(c)
// fatal error: Float value cannot be converted to Int because the result would be greater than Int.max

您的值 Float(Int.max) 也会发生同样的情况,即 浮点可表示值 最接近Int.max 并且发生 大于Int.max

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 2018-12-23
    相关资源
    最近更新 更多