f(int x){return x == 0?0:1; 在没有条件的Java中

Cro*_*yer 31 java bit-manipulation

我想f(int x) { return x == 0 ? 0 : 1; }用Java 实现.

在C中,我只是" return !!x;",但!在Java中并不像那样.有没有办法在没有条件的情况下做到这一点?没有像展开版本那样俗气的东西

int ret = 0;
for (int i = 0; i < 32; i++) {
    ret |= ((x & (1 << i)) >>> i);
}
Run Code Online (Sandbox Code Playgroud)

要么

try {
   return x/x;
} catch (ArithmeticException e) {
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

)

编辑:

所以,我做了三个不同解决方案的微基准测试:

  1. 我的返回x/x catch解决方案,
  2. 明显的x == 0?0:1解决方案,和
  3. Ed Staub的解决方案:(x | -x)>>> 31.

随机int输入的时间(整个int范围)是:

1. 0.268716  
2. 0.324449  
3. 0.347852  
Run Code Online (Sandbox Code Playgroud)

是的,我的愚蠢的x/x解决方案的速度非常快.当您考虑到其中只有很少的0时,并不是很令人惊讶,并且在绝大多数情况下都会采用快速路径.

50%输入为0的更有趣案例的时间安排:

1. 1.256533  
2. 0.321485  
3. 0.348999  
Run Code Online (Sandbox Code Playgroud)

天真的x==0?0:1解决方案比聪明的解决方案快了约5%(在我的机器上).我明天会尝试做一些拆卸,找出原因.

EDIT2: 好的,所以条件版本的反汇编是(不包括簿记):

testl rsi,rsi
setnz rax
movzbl rax,rax
Run Code Online (Sandbox Code Playgroud)

(x | -x)>>> 31的反汇编是:

movl rax,rsi
negl rax
orl rax,rsi
sarl rax,#31
Run Code Online (Sandbox Code Playgroud)

我认为还有什么需要说的.

Ed *_*aub 42

好吧,没有条件的最短解决方案可能是:

return (i|-i) >>> 31;
Run Code Online (Sandbox Code Playgroud)

  • 如果你真的想要摆脱majick 31,正确的方法是返回`(i | -i)>>>(Integer.SIZE-1);`.不过,我当然不会出于前向兼容性问题而这样做 - 只是为了明确31来自哪里.有太多,太多的代码会破坏他们永远改变int的大小. (15认同)
  • @ eng.Fouad,谈论快速解决方案,并用不少于2个函数调用将其删除. (5认同)
  • @Ed Staub:这不是十几个人物.它知道如何处理它们. (5认同)

Eng*_*uad 11

这是一个解决方案:

public static int compute(int i)
{
    return ((i | (~i + 1)) >> 31) & 1; // return ((i | -i) >> 31) & 1
}
Run Code Online (Sandbox Code Playgroud)

编辑:

或者你可以使它更简单:

public static int compute(int i)
{
    return -(-i >> 31); // return -i >>> 31
}
Run Code Online (Sandbox Code Playgroud)

EDIT2:

最后的解决方案失败,负数.看看@Ed Staub的解决方案.

EDIT3:

@Orion Adrian OK,这是一个通用解决方案:

public static int compute(int i)
{
    return (i|-i) >>> java.math.BigInteger.valueOf(Integer.MAX_VALUE).bitLength();
}
Run Code Online (Sandbox Code Playgroud)

  • @Orion Adrian:这会发生什么时候?由于Java具有向后兼容性,我怀疑他们是否会改变int的位长.他们可能只是为128位数字添加一个新类型`superlong`. (4认同)

phl*_*tos 8

int f(int x) {
    return Math.abs(Integer.signum(x));
}
Run Code Online (Sandbox Code Playgroud)

signum()函数返回数字的符号为-1,0或1.所以剩下的就是将-1变为1,这就是做什么abs.

  • _如果对有条件的反对是基于性能问题,那么这种治疗肯定比疾病更糟糕. (4认同)
  • 这有点欺骗,因为Math.abs内部有条件:p (4认同)
  • int f(int x){Integer.signum(x)&1;}会有点笨拙. (3认同)

Yan*_*hon 7

signum函数以这种方式实现它

return (i >> 31) | (-i >>> 31);
Run Code Online (Sandbox Code Playgroud)

所以,只需添加另一个按位操作返回01

return ((i >> 31) | (-i >>> 31)) & 1;
Run Code Online (Sandbox Code Playgroud)


ncm*_*ist 7

所有这些解决方案似乎都受到了不同程度努力去理解的弊端.这意味着必须稍后阅读和维护此代码的程序员将不得不花费不必要的努力.这要花钱.

表达方式

(x == 0)? 0:1
Run Code Online (Sandbox Code Playgroud)

很简单易懂.这是真正做到这一点的正确方法.在普通的代码运行中使用异常是非常可怕的.例外情况是处理程序员控制之外的情况,而不是普通的日常操作.

  • 完全正确,除非在极端性能要求方面,如图像处理.在这种情况下,使用我在"最终"方法中建议的解决方案应该消除可能导致管道停滞的任何分支.但我认为这主要是作为一个难题. (5认同)