博主今日在一道算法题中调用自定义的快速幂方法时,提交始终无法AC
。但算法思路和实现过程万无一失,经过仔细排查,终于发现了一个一直以来都被忽视掉的Java基本数据向上转型溢出问题。
话不多说,直接上代码:
public class Int2LongOverflow {
public static void main(String[] args) {
// Integer.MAX_VALUE = 2147483647
int a = Integer.MAX_VALUE;
int b1 = 1;
long sum1 = a + b1;
long sum2 = 2147483647 + 1;
long b2 = 1;
long sum3 = a + b2;
long sum4 = 2147483647 + 1L;
System.out.println("sum1 = " + sum1);
System.out.println("sum2 = " + sum2);
System.out.println("sum3 = " + sum3);
System.out.println("sum4 = " + sum4);
}
}
在公布答案之前,大家思索一下,是不是sum1, sum2, sum3, sum4
均输出2147483648
呢?
好了,下面是代码运行结果:
sum1 = -2147483648
sum2 = -2147483648
sum3 = 2147483648
sum4 = 2147483648
看似sum1, sum2, sum3, sum4
均计算2147483647 + 1
的结果,并且结果用long
类型接收,但为什么会有两种不同计算结果呢?
sum1
计算的是a + b1
,其中a
和b1
均为int类型数据。因此,计算顺序是这样的:首先取表达式中最大的类型暂存计算结果,也就是int类型暂存a + b1
的结果,显然会发生溢出,暂存的结果就为-2147483648
;然后再将暂存的结果赋值给sum1
,其中会向上隐式转型,于是sum1 = -2147483648
。sum2
计算的是两个字面量值的和。在Java中,纯数字字面量默认类型为int(在整数后加L
表示long类型,例如12L
表示long类型的12
),因此,sum2
的计算效果同sum1
。sum3
与sum1
唯一的区别就是,其中b2
的类型变为long
(值保持不变)。因此在计算时,会将a
向上转型为long
,再做long
类型之间的运算,运算结果为long
类型,最后再赋值给sum3
。这一系列操作自然不会发生溢出。sum4
表达式中的字面量1L
为long类型,计算效果同sum3
,不会发生溢出。
总结:
在Java中,如果赋值语句的右边是表达式,可以将该语句看成三个执行过程。
- 将表达式中所有变量或字面量隐式向上转型为其中的最大类型;
- 用1中的最大类型暂存计算结果;
- 将暂存的结果赋值给左侧变量,如果暂存类型与左侧变量类型不一致,需要做显式(向下转型)或隐式(向上转型)的类型转换。