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

时间:2017-04-30 12:57:55

标签: swift floating-point type-conversion

我发现了一些非常奇怪的事情。如果你在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。 / p>

我也用Double尝试了这个,它也崩溃了。

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

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

打印预期的9223372036854775807!

swift出了什么问题?

2 个答案:

答案 0 :(得分:7)

DoubleFloat的尾数中没有足够的位来准确表示19有效数字,因此您得到的是舍入结果。

如果使用Float打印String(format:),您可以更准确地看到Float的值:

let a = Int.max
print(a)                          // 9223372036854775807
let b = Float(a)
print(String(format: "%.1f", b))  // 9223372036854775808.0

因此Float代表的值1大于Int.max

许多值将转换为相同的Float值。问题是,在Int.max导致不同的DoubleFloat值之前,您需要减少多少{。}}。

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' s)。 32位的Float不能存储63位的尾数,因此必须进行舍入或截断。在Int.max的情况下,向上舍入为1会得到该值 1000000000000000000000000000000000000000000000000000000000000000。从左边开始,尾数只表示1个有效位(尾随0' s是免费的),所以这个数字是1的尾数和指数64

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

答案 1 :(得分:1)

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