1. 概述

在Java编程中,理解如何操作整数是编写健壮且高效代码的基础。一个常见的操作就是对整数取反。

在这个教程中,我们将探讨对整数取反的不同方法。

2. 问题介绍

取反一个整数意味着将它的符号从正变负或反之。 例如,给定一个int值42,取反后我们期望得到-42作为结果。

我们不应忘记数字0既不是正也不是负。 因此,取反0的结果也应该是0。

在Java中,这个操作相当直接,我们将看到三种不同的实现方式。此外,我们还会讨论一个边缘情况:整数溢出。

为了简化,我们将使用单元测试断言来验证每种方法的结果。

3. 使用一元减号运算符

对整数取反的最直接方法是使用一元减号运算符(-)。它简单地改变给定整数的符号

int x = 42;
assertEquals(-42, -x);

int z = 0;
assertEquals(0, -z);

int n = -42;
assertEquals(42, -n);

如测试所示,通过应用'-'到输入整数,我们得到了预期的结果。

4. 使用按位补码运算符

另一种非传统但有效的方法是使用按位补码运算符(~)。这个运算符会反转给定整数的位,从而产生二进制补码

int number = 12;
int negative13 = ~number; // ~00001100 = 11110011 = -13

因此,对于一个整数x,~x + 1就是x的取反。

接下来,让我们编写一个测试来验证这一点:

int x = 42;
assertEquals(-42, ~x + 1);

int z = 0;
assertEquals(0, ~z + 1);

int n = -42;
assertEquals(42, ~n + 1);

正如我们所见,*~x + 1*解决了这个问题。

5. 涉及 Integer.MIN_VALUE 的溢出问题

我们知道Java的整数类型是一个有符号的32位类型,范围从-2147483648到2147483647。如果取反Integer.MAX_VALUE,结果-2147483647仍然在这个范围内。但是,如果我们取反Integer.MIN_VALUE,我们应该得到2147483648,这超出了Integer.MAX_VALUE的范围。 因此,在这个边缘情况下,会发生溢出错误。

虽然'-x'和'~x + 1'方法很简单,只有在确保不会发生溢出的情况下,我们才能在应用程序中使用它们。 下面是一些可能适合使用这些方法的场景:

  • 计算足球比赛中的进球差分
  • 计算员工一个月的工作小时数

然而,如果我们的程序中可能出现溢出,不建议使用这些方法。

接下来,我们将探讨为什么在使用Integer.MIN_VALUE作为输入时,这两种方法会导致溢出错误。

5.1. 使用一元减号运算符与Integer.MIN_VALUE

首先,我们使用'-'运算符取反Integer.MIN_VALUE

int min = Integer.MIN_VALUE;
LOG.info("The value of '-min' is: " + -min);
 
assertTrue((-min) < 0);

这个测试通过了,这意味着我们取反Integer.MIN_VALUE后仍然得到了一个负结果。我们可以通过输出进一步验证这一点:

The value of '-min' is: -2147483648

因此,当发生溢出时,'-x'方法返回错误的结果。

5.2. 使用按位补码运算符与Integer.MIN_VALUE

现在,让我们使用'~x + 1'方法运行同样的测试:

int min = Integer.MIN_VALUE;
int result = ~min + 1;
LOG.info("The value of '~min + 1' is: " + result);
 
assertFalse(result > 0);

我们可以看到,当发生溢出时,这个方法也不会给出预期的结果。 让我们进一步检查控制台日志输出以确认这一点:

The value of '~min + 1' is: -2147483648

6. 使用 Math.negateExact() 方法

对于必须处理Integer.MIN_VALUE的情况,Math.negateExact()方法提供了一个安全且精确的方式来取反整数。

首先,Math.negateExact()在正常情况下工作正常:

int x = 42;
assertEquals(-42, Math.negateExact(x));

int z = 0;
assertEquals(0, Math.negateExact(z));

int n = -42;
assertEquals(42, Math.negateExact(n));

接下来,让我们看看当输入是Integer.MIN_VALUE时会发生什么:

int min = Integer.MIN_VALUE;
assertThrowsExactly(ArithmeticException.class, () -> Math.negateExact(min));

如测试所示,如果在取反过程中发生溢出,Math.negateExact()方法会抛出ArithmeticException,开发者可以在异常发生时进行处理。

7. 总结

在这篇文章中,我们探讨了Java中对整数取反的三种方法。

'-x'和'~x + 1'是直接的解决方案。然而,如果我们的程序可能需要取反Integer.MIN_VALUE,那么使用Math.negateExact()是正确的选择

如往常一样,示例代码的完整源代码可在GitHub上找到:GitHub链接