在C#中的位运算

3

有没有比这个更短更好看的替代方案?

(b == 0) ? 0 : 1;

在位运算方面?

此外,为了获得给定整数a的正确符号(-1、0或1),我目前正在使用以下方法

(a > 0) ? 1 : (a >> 32);

有没有更短但不会更慢的方法?

你必须向左移31位,而不是32位。如果你认为它比Math.Sign()更快,那么交换测试。 - Hans Passant
3个回答

6

个人建议您选择第一种选项来处理“等于零或不等于零”的情况。

对于整数的符号,我建议使用Math.Sign函数,并假设JIT编译器会将其内联 - 如果它被证明是一个潜在的瓶颈,则通过基准测试来验证这个假设。

最重要的是考虑可读性 - 您的第一段代码非常明显。 第二个则不然。 我甚至不确定您的第二段代码是否有效... 如果这是一个Int32int),那么我认为右移操作实际上会被掩码到底部的5位。

编辑:刚刚检查了一下,事实上您当前的第二段代码等价于:

int y = x > 0 ? 1 : x;

这种转换甚至不会出现在编译后的代码中。

将其视为一个关于更加注重可读性而非微观优化的教训。 如果代码易于理解,那么使代码运行起来就容易得多。如果你的代码给出了错误的结果,那么它运行得多快我也不在意。


1
根据我的测试,Math.Sign 比我正在使用的代码慢得多。但是,再次强调,我希望得到关于位运算的答案,而不是替代方案,因为我正在学习!无论如何,非常感谢您。 - Miguel
1
@Miguel:你能发布这些测试吗?有很多方法会导致基准测试出现错误... - Jon Skeet
2
@Miguel:请参阅C# 4规范,第7.9节:“当x的类型为intuint时,移位计数由count的低五位给出。换句话说,移位计数是通过count&0x1f计算的。”具有讽刺意味的是,在我注释过的规范版本中,我在下面刚好有一个注释,说“掩码移位计数可能很有效,但它可能会导致一些非常令人困惑的行为。”看来我的注释是正确的 :) - Jon Skeet
@Scott:但是这种语言本可以被设计成完全忽略CPU限制,定义返回值并采取任何必要措施来实现。JIT是否无法检测到一个值正在超出类型的宽度,并相应地返回0或-1?绝对不是。在我看来,这将是更自然和一致的结果...而且移开自然和一致的定义(当可以实现时)的唯一原因就是为了性能。 - Jon Skeet
@Scott:考虑到有多少其他事情已经改变,我不明白为什么他们会因为历史原因而保留这个缺陷。这些“干净”的语义是以何种方式呈现的?(我似乎记得 ECMA 注释规范支持我的观点,但我目前找不到我的副本。) - Jon Skeet
显示剩余8条评论

4
微小优化是万恶之源。你为了一个纳秒的效率牺牲了可读性,这是一个糟糕的交易。

3
@Miguel:真正认识到微观优化是有害的事实,比学习一些位运算技巧更加有用,相信我。 - Armen Tsirunyan

1
在位操作方面...有人能告诉我吗?此外,为了获取给定整数a的正确符号(-1、0或1),我目前正在使用的方法是
(a > 0) ? 1 : (a >> 32); 

Armen Tsirunyan和Jon Skeet回答了你的技术问题,我将尝试解释一些你似乎存在的技术误解。

第一个错误是,如果你有一个32位有符号整数,并尝试将其移位32位,那么你将尝试查看第33位,而在有符号二进制算术中,这将是一个溢出位。

第二个错误是当你有一个32位有符号二进制值时。最后一位要么是1,要么是0。只有一个零值。因此,你试图弄清楚符号是否为(-1,0,1)的陈述明显表明你不理解这个事实。如果有符号位是1,则数字为负数;如果为0,则为正数。处理大多数.NET Framework中的数字的结构并不知道2的补码和1的补码。当然,这并不意味着你不能扩展该功能或者简单地将有符号整数转换为2的补码数(非常简单)。

我应该补充说明,当你有一个有符号整数时,只有一个零值。我想这就是你所说的“检查符号”语句中存在误解二进制数的主要问题。

http://en.wikipedia.org/wiki/Signed_magnitude#Sign-and-magnitude


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