1. 概述

在这个教程中,我们将探讨如何计算两个给定整数之间的绝对差。

2. 使用 Math.abs() 方法

问题很简单。让我们通过几个例子快速理解:

  • num1=3, num2=4: absDiff=1
  • num1=3, num2=-4: absDiff=7
  • num1=-3, num2=-4: absDiff=1

从上面的例子可以看出,*给定两个整数 num1num2,结果是 (num1 - num2) 的绝对值。* 此外,Java 标准库提供了 Math.abs() 方法来返回一个数的绝对值。因此,我们可以轻松地将计算转换为 Java 代码:

public int absDiff(int num1, int num2) {
    return Math.abs(num1 - num2);
}

如我们所见,absDiff() 方法返回结果,并打印出来。

为了简化,让我们使用单元测试断言来验证方法是否按预期工作:

@Test
public void testAbsDiff() {
    assertEquals(1, absDiff(3, 4));
    assertEquals(7, absDiff(3, -4));
    assertEquals(1, absDiff(-3, -4));
}

当我们运行测试时,它会通过。所以,我们已经解决了这个问题。然而,这个实现隐藏了一个潜在问题。接下来,让我们解决它。

3. 浮动/下溢问题

我们知道 Java 的 int 是一个有符号的 32 位整数。换句话说,Java 整数的范围是 -21474836482147483647

现在,让我们回顾一下我们的实现。我们有计算:num1 - num2。因此,当我们将 Integer.MAX_VALUE-200 传递给 absDiff() 方法时,结果可能会超过 Integer.MAX_VALUE。接下来,让我们用这两个数字调用该方法看看会发生什么。

首先,手动计算一下预期结果:

Result = Integer.MAX_VALUE - (-200)
       = 2147483647 + 200
       = 2147483847

然而,如果我们调用 absDiff() 方法,不会出现异常,而是看到输出:

Absolute diff: 2147483449

显然,结果不正确。这是因为发生了整数溢出(overflow)。 当然,计算结果也可能会小于 Integer.MIN_VALUE,例如 absDiff(Integer.MIN_VALUE, 200)。在这种情况下,我们称之为下溢。

接下来,我们将探讨如何解决溢出/下溢问题。

4. 使用不同的数据类型

在 Java 中,有一些范围大于 int 的数字类型,如 longBigInteger。为了避免溢出/下溢问题,我们可以在执行计算之前将 int 参数转换为这些类型之一。以 long 为例,我们在新方法中实现逻辑:

public int absDiff(int num1, int num2) {
    long diff = Math.subtractExact(num1, num2);
    return (int) Math.abs(diff);
}

现在,让我们测试当我们将 Integer.MAX_VALUE-200 传递给它时,它是否能给出预期的结果:

long diff = absDiffAsLong(Integer.MAX_VALUE, -200);
assertEquals(Integer.MAX_VALUE + 200L, diff);

如果运行测试,它会通过。因此,这种方法解决了溢出/下溢问题。但是值得注意的是,返回类型是 long 而不是 int

如果调用者期望结果为 long,那没关系。否则,如果需要 int,我们必须将 long 值转换为 int。这很不方便。而且,如果我们错误地进行 longint 的转换,也可能发生溢出/下溢问题。

接下来,我们将看看是否可以保持结果为 int 并避免溢出/下溢问题。

5. 当溢出/下溢发生时抛出异常

假设我们的程序需要整数形式的绝对差结果。那么,方法的理想行为是返回一个 int,并在发生溢出/下溢时抛出异常

然而,正如我们之前看到的,Java 在普通的 (num1 - num2) 计算过程中不会在溢出时抛出异常。这使得当我们发现程序没有产生预期结果时,很难找到真正的原因。

自 Java 8 开始,Java 的 Math 提供了新的 精确运算方法。这些方法在溢出/下溢发生时抛出 ArithmeticException。因此,让我们将 absDiff() 方法中的 (num1 - num2) 替换为 subtractExact()

public int absDiff(int num1, int num2) {
    long diff = Math.subtractExact(num1, num2);
    return (int) Math.abs(diff);
}

最后,以下测试表明 absDiff2() 如预期工作:

int diff1 = absDiff2(100, -200);
assertEquals(300, diff1);

int diff2 = absDiff2(100, 200);
assertEquals(100, diff2);

//overflow -> exception
assertThrows(ArithmeticException.class, () -> absDiff2(Integer.MAX_VALUE, -200));

6. 总结

在这篇文章中,我们探讨了计算两个整数之间的绝对差。此外,我们讨论了溢出/下溢问题。

如往常一样,文章中展示的所有代码片段可在 GitHub 上获取。