不使用Math.abs()方法找到一个数的绝对值

23

有没有一种方法可以在Java中不使用Math.abs()方法来找到一个数的绝对值。


27
不想使用那种方法的原因是... - Thilo
这个数字是指定为整数类型,如int、byte、short、long,还是浮点类型(float、double),或者是装箱类(Integer、Double等)或BigDecimal、BigInteger或其他类型?未指定? - user unknown
我需要在循环中使用它,因此我正在寻找任何其他最佳方法。 - Theja
1
你可以在循环中使用 Math.abs。不要过度优化,JVM通常会使其足够快。如果你真的认为它太慢了,那就进行测量。 - Thilo
@Thilo 我已经检查过了,运行良好。我正在尝试寻找不同的方法,以便根据我的需求使用最佳方法。 - Theja
10个回答

61

如果你查看 Math.abs 的内部实现,你可能会找到最好的答案:

例如,对于浮点数:

    /*
     * Returns the absolute value of a {@code float} value.
     * If the argument is not negative, the argument is returned.
     * If the argument is negative, the negation of the argument is returned.
     * Special cases:
     * <ul><li>If the argument is positive zero or negative zero, the
     * result is positive zero.
     * <li>If the argument is infinite, the result is positive infinity.
     * <li>If the argument is NaN, the result is NaN.</ul>
     * In other words, the result is the same as the value of the expression:
     * <p>{@code Float.intBitsToFloat(0x7fffffff & Float.floatToIntBits(a))}
     *
     * @param   a   the argument whose absolute value is to be determined
     * @return  the absolute value of the argument.
     */
    public static float abs(float a) {
        return (a <= 0.0F) ? 0.0F - a : a;
    }

20

是的:

abs_number = (number < 0) ? -number : number;

对于整数,这个方法很好用(除了 Integer.MIN_VALUE,因为它的绝对值无法表示为一个 int)。
对于浮点数,情况就更加微妙了。例如,这个方法 - 以及目前发布的所有其他方法 - 都无法正确处理负零
为了避免自己处理这些微妙之处,我的建议是坚持使用 Math.abs()

@Thilo:真正的重点在于浮点数运算充满了微妙之处。除非有真正令人信服的理由,否则应该坚持使用标准函数。 - NPE
@userunknown:当然,但这是整数的二进制补码表示的属性,而不是计算abs()方法的属性。 - NPE
@userunknown:如果你仔细阅读问题,OP想要模拟Math.abs()。我回答中的代码对于Integer.MIN_VALUE的行为与Math.abs(int)完全相同 - NPE
@aix:也许你可以在聊天室里来找我们?我没有看到“模拟 Math.abs”这句话,我是说不使用 Math.abs。而且我认为 Javalibs 中的方法因为这个特定原因而有问题。 - user unknown
@userunknown:感谢邀请,但这个问题对我个人来说并不重要,无法为进一步辩论投入时间。 - NPE
显示剩余4条评论

13

像这样:

if (number < 0) {
    number *= -1;
}

我知道一个测试用例会导致这个失败。 - user unknown
@userunknown,你是在指MIN_VALUE吗? - tibtof
2
@userunknown MIN_VALUE的正值无法被包含在相同类型的数据中,因此这不是一种流程问题。请看这里:http://en.wikipedia.org/wiki/Two%27s_complement - tibtof
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/12491/discussion-between-user-unknown-and-tibtof - user unknown

5

由于Java是一种静态类型语言,如果一个abs方法接受一个int参数,则应该返回一个int; 如果它接受一个float,则应该返回一个float; 对于Double,则应该返回一个Double。也许它可以始终返回double和Double等的装箱或拆箱类型。

因此,您需要为每种类型编写一个方法,但现在您面临一个新问题:对于byte、short、int、long,负值范围比正值范围大1。

那么这个方法应该返回什么呢?

byte abs (byte in) {
   // @todo
}

如果用户对-128调用abs?您可以始终返回下一个更大的类型,以确保范围适合所有可能的输入值。这将导致长时间出现问题,在那里不存在普通的更大类型,并使用户在测试后始终将值降低 - 可能会有麻烦。
第二个选项是抛出算术异常。这将防止强制转换并检查返回类型,用于已知输入受限的情况,例如表示为int的MONTH.
byte abs (byte in) throws ArithmeticException {
   if (in == Byte.MIN_VALUE) throw new ArithmeticException ("abs called on Byte.MIN_VALUE"); 
   return (in < 0) ? (byte) -in : in; 
}

“忽略MIN_VALUE的罕见情况”习惯不是一个选项。首先让代码能够工作 - 然后再使其更快。如果用户需要更快但有缺陷的解决方案,他应该自己编写。最简单的可能有效的解决方案是:简单,但不要过于简单。

由于代码不依赖于状态,因此该方法可以并且应该被设置为静态。这允许进行快速测试:

public static void main (String args []) {
    System.out.println (abs(new Byte ( "7")));
    System.out.println (abs(new Byte ("-7")));
    System.out.println (abs((byte)  7));
    System.out.println (abs((byte) -7));
    System.out.println (abs(new Byte ( "127")));
    try
    {
        System.out.println (abs(new Byte ("-128")));
    }
    catch (ArithmeticException ae)
    {
        System.out.println ("Integer: " + Math.abs (new Integer ("-128")));
    }
    System.out.println (abs((byte)  127));
    System.out.println (abs((byte) -128));
}

我捕捉第一个异常并将其放入第二个异常中,只是为了演示。

编程中存在一种不好的习惯,即程序员更关注快速而不是正确的代码。真遗憾!


如果你好奇为什么负值比正值多一个,我有一个 图表供您参考


2
尽管现代处理器上的分支问题通常不是瓶颈,但在整数的情况下,您可以选择无分支解决方案,如此处所述:http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs
(x + (x >> 31)) ^ (x >> 31);

然而,在 Integer.MIN_VALUE 的明显情况下,这种方法会失败,因此这是一种使用风险自负的解决方案。


是的,如果你想要让很多人困惑,特别是如果你把函数命名为a()或类似含糊的名称,那么这非常棒。 - niken

1

如果要在Java中计算一个整数x的绝对值,且不使用Math.abs()、条件或位运算符,下面是可能的解决方案。

(int)(((long)x*x - 1)%(double)x + 1);

因为Java将a%b视为a-a/b * b,所以结果的符号将与“a”相同,无论“b”的符号如何;(x*x-1)%x将等于abs(x)-1;对“long”进行类型转换是为了防止溢出,而double允许除以零。
同样,x = Integer.MIN_VALUE将导致溢出,因为会进行减1操作。

0

您可以使用:

abs_num = (num < 0) ? -num : num;

0
这是一个一行代码的解决方案,它将返回一个数的绝对值。
abs_number = (num < 0) ? -num : num;

0

-num将在Integer.MIN_VALUE情况下等于num

 Integer.MIN_VALUE =  Integer.MIN_VALUE * -1

0
假设N是您想要计算绝对值(没有符号的正数)的数字。
if (N < 0)
    {
        N = (-1) * N;
    }

N现在将返回绝对值


8
题目明确要求“不使用 Math.abs()”函数。 - Kenster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接