R 整数乘法溢出为何返回 NA,而不自动转换为 double?
结论
对于普通的 R 数值对象,两个 integer 使用 * 相乘时,结果仍按 integer 类型处理。如果乘积超出可表示范围,R 会返回 NA_integer_ 并发出警告,不会因溢出而自动改用 double。可以在相乘前将至少一个操作数转换为 double。
这一行为早在 R 4.5.2 之前就已存在。2018 年的 R-help 讨论中已经明确说明了整数乘法溢出返回 NA 的规则,无需将它归因于所列的 Windows 环境。
为什么这两个数会溢出?
原始输出已确认 typeof(x) 和 typeof(y) 都是 "integer",因此 x * y 执行的是整数乘法。
R 的普通 integer 使用 32 位存储,在 64 位系统上也是如此,参见 R 的 integer 文档。其最大非缺失值为:
.Machine$integer.max
# [1] 2147483647
题中的数学乘积是:
21475 × 100000 = 2147500000
2147500000 > 2147483647
两个操作数各自都能存入 integer,乘积却超过了上限,因此出现了原始警告:
NAs produced by integer overflow
这里的 NA 是运算溢出产生的,并不表示输入中原本就有缺失值。
如何得到正确的乘积?
在运算前显式转换一个操作数即可。下面用带 L 后缀的常量重建题中的整数输入:
x <- 21475L
y <- 100000L
result <- as.double(x) * y
print(result, digits = 15)
# [1] 2147500000
typeof(result)
# [1] "double"
也可以写成 x * as.double(y)。对于这里的普通整数对象,as.numeric(x) * y 同样适用。as.numeric() 与 as.double() 的关系见 R 的 double 文档。
转换必须发生在乘法之前。下面这种写法是在相乘之后才转换:
as.double(x * y)
R 会先计算括号中的整数乘法,溢出得到 NA,再将这个 NA 转换为 double。此时转换无法恢复已经丢失的乘积。
为什么平时感觉 R 会自动转换?
常见原因是普通数值常量默认就是 double,即使它没有小数部分:
typeof(100000)
# [1] "double"
typeof(100000L)
# [1] "integer"
因此,下面的乘法能够得到结果:
21475L * 100000
# [1] 2147500000
这里在运算开始时就有一个 double 操作数。类型转换由操作数类型决定,并不是先发生整数溢出,再自动重算。
不同运算符的规则也不同:两个 integer 使用 / 或 ^ 时会返回 double;使用 +、-、*、%%、%/% 时则保留整数结果类型。这些规则记录在 R 的 Arithmetic 文档中。
转成 double 后还有什么限制?
本例的乘积 2147500000 可以用 double 精确表示。但 double 仍有精度和范围限制,并非任意精度整数。按照其 53 位有效二进制精度,绝对值不超过 2^53 的整数都可以精确表示;超过这个范围后,就不能保证每个整数都能精确保存,参见 double 精度说明。
如果后续计算需要精确处理更大的整数,应使用支持相应整数范围或任意精度整数的类型。同时,不要再把本例的结果转回普通 integer,它仍然超过该类型的上限。
备注:内容仅供参考。
