使用模运算测试整数的奇偶性

26
以下代码片段未正确测试奇偶性:
public static boolean isOdd(int i) {
   return i % 2 == 1;
}

我在网上读到应该按照以下方式进行:

public static boolean isOdd(int i) {
   return i % 2 != 0;
}

为什么会这样呢?


使用取模运算来测试奇偶性有点过度了。 - Danubian Sailor
2
非常重要的是,Java 没有取模运算符。% 是求余数运算符。http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.3 - djeikyb
1
模数运算符和余数运算符有什么区别? - kaspersky
6个回答

34

可能是因为(i % 2) != 0适用于正数和负数。


3
好的,但是根据运算符优先级,括号()是多余的,我是说你可以忽略它。 - Grijesh Chauhan
30
我不怀疑你,@GrijeshChauhan,但我发现使用()可以更方便地避免在我学习的所有语言中记忆所有运算符优先级规则!我认为这也有助于阅读性,因为它消除了和我一样处于同样境地的人们的任何疑虑 :-). - Stochastically
同意,() 只是用于覆盖运算符优先级,为什么要记住呢 :) - Grijesh Chauhan

19
因为当 i 是负数时 --> (-1) % 2 == -1

12

您应该使用:

(i & 1) != 0
为了避免符号问题。
另外请注意,使用&可以确保编译器无论多么愚蠢,它都永远不会尝试使用除法来实现%操作。

同意,i%1始终为0。 - Stochastically
糟糕!老年痴呆了 - 应该是 i & 1 - OldCurmudgeon
3
@GradyPlayer 的“will”是非常强烈的表述方式;要么提供数据证明,要么就别说。一个快速的测试表明,在 AMD64 上,Debian 的 GCC 4.4.5 编译器为 (i & 1) != 0(i % 2) != 0 这两个语句生成了完全相同的汇编代码。代码如下:#include <stdio.h> int main(void) { volatile int i = 1; volatile int j = (i & 1) != 0; printf("%d", j); return j; }(如果把 i&1 改成 i%2 结果也是一样)。使用 cc -Sdiff -u 命令比较两个汇编文件,发现唯一的区别在于 .file 指令(因为我为每个文件都创建了一个不同的文件名)。 - user
@MichaelKjörling并不令人惊讶,编译器开发人员应该已经想出了优化的方法...这是一个非常普遍的测试。 - Grady Player
2
@GradyPlayer:-O0基本上意味着没有优化。这对于调试很有用,但将其选择为基准进行基准测试... 至少可以说是有问题的。 - Maciej Piechotka
@GradyPlayer 我用 -O0-O3 两种选项进行了测试,结果完全相同。 - user

8

如果将取模运算符替换为按位与运算符,则第一个代码片段将正常工作:

public static boolean isOdd(int i) {
   return (i & 1) == 1;
}

6

这与Java中的模运算方式有关。如果i是负数,答案也将是负数。每个负输入都会返回false。


1
由于以下方法在传递每个数字参数的负数、零和正数时表现良好。
public static boolean isOdd(int i) {
   return i % 2 != 0;
}

如果我们使用上述方法isOdd(),性能可能不太好,最好使用位运算符AND(&)代替remainder(%)运算符。
 public static boolean isOdd(int i) {
       return (i & 1) != 0;
    }

这段代码比第一个更快,具体取决于我们使用的平台和虚拟机。

注意:通常情况下,除法和取余运算符与其他算术和逻辑运算符相比较慢。


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