为何Kotlin中大范围数值乘积得0?(1..100)与(1..10)reduce运算对比
为什么Kotlin中大范围数值乘积会得到0?
嘿,这个问题其实戳中了整数类型的一个核心特性——溢出截断,我来给你一步步理清楚:
1. 核心原因:32位Int的溢出与二进制特性
Kotlin里默认的Int是32位有符号整数,它的取值范围是 -2^31(也就是-2147483648)到 2^31-1(2147483647)。当你计算的乘积超过这个范围时,并不会抛出错误,而是会发生溢出截断:超出32位的部分会被直接丢掉,只保留低32位的数值。
而当你计算大范围的乘积(比如100的阶乘)时,这个乘积里包含了大量的2和5的因子(毕竟从1到100里有很多偶数和5的倍数),这意味着它的二进制表示末尾会有非常多的0。当溢出发生时,高位被截断,剩下的低32位全是0,最终结果自然就变成0了。
2. 为什么1..10和1..100的结果不一样?
- 先看
(1..10).toList().reduce { acc, i -> acc * i }:10的阶乘是3628800,这个数远小于2^31-1(2147483647),完全在32位Int的容纳范围内,所以不会溢出,结果正确。 - 再看
(1..100).toList().reduce { acc, i -> acc * i }:100的阶乘是一个极其巨大的数,早就超出了32位Int的范围。而且如刚才所说,它的二进制末尾有大量的0,溢出截断后低32位全是0,所以结果就是0。
怎么解决这个问题?
如果你想得到正确的大数值乘积结果,可以用更大的整数类型或者任意精度的大数类:
- 用
Long(64位整数):把其中一个操作数转成Long,整个计算就会以Long类型进行,比如:
不过Long也有范围限制(最大9223372036854775807),100的阶乘其实也超过了Long的范围,所以如果要完全正确,得用:(1..100).toList().reduce { acc, i -> acc * i.toLong() } - 用
BigInteger(任意精度大数):
这样就能得到100阶乘的准确值啦。import java.math.BigInteger (1..100).map { it.toBigInteger() }.reduce { acc, i -> acc * i }
内容的提问来源于stack exchange,提问作者Muhammad Lukman Low
相关产品推荐
相关产品推荐

