rmn*_*rmn 20 c++ numbers readability bitwise-operators low-level
那么,至少有两种低级方法可以确定给定的数字是否是偶数:
1. if (num%2 == 0) { /* even */ }
2. if ((num&1) == 0) { /* even */ }
Run Code Online (Sandbox Code Playgroud)
我认为第二种选择更加优雅和有意义,而这正是我经常使用的选择.但这不仅仅是品味问题; 实际性能可能会有所不同:通常按位操作(例如logial和here)比mod(或div)操作更有效.当然,你可能会争辩说有些编译器无论如何都能优化它,我同意......但有些人不会.
另一点是,对于经验不足的程序员来说,第二个可能有点难以理解.关于这一点,我回答说,如果这些程序员花很短的时间来理解这种语句,它可能只会让每个人受益.
你怎么看?
只有当num无符号整数或具有二进制补码表示的负数时,给定的两个片段才是正确的. - 正如一些评论所说的那样.
jas*_*son 76
我首先编码可读性,所以我的选择是num % 2 == 0.这比清楚得多num & 1 == 0.我会让编译器担心我的优化,只有在分析显示这是一个瓶颈的情况下才会调整.其他任何事情都为时过早.
我认为第二种选择更加优雅和有意义
我强烈反对这一点.一个数字甚至是因为它的模2的一致性为零,而不是因为它的二进制表示以某一位结尾.二进制表示是一个实现细节.依赖于实现细节通常是代码味道.正如其他人所指出的那样,在使用补码表示的机器上测试LSB失败了.
另一点是,对于经验不足的程序员来说,第二个可能有点难以理解.关于这一点,我回答说,如果这些程序员花很短的时间来理解这种语句,它可能只会让每个人受益.
我不同意.我们都应该编码以使我们的意图更清晰.如果我们正在测试均匀性,代码应该表达(并且评论应该是不必要的).同样,测试一致性模2更清楚地表达了代码的意图而不是检查LSB.
而且,更重要的是,细节应隐藏在isEven方法中.所以我们应该看到if(isEven(someNumber)) { // details }并且只num % 2 == 0在定义中看到一次isEven.
Ste*_*sop 24
如果你要说一些编译器不会优化%2,那么你还应该注意到一些编译器使用一个用于有符号整数的补码表示.在该表示中,&1 给出负数的错误答案.
那么你想要什么 - "一些编译器"的代码很慢,或者"某些编译器"的代码是错误的?在每种情况下不一定是相同的编译器,但这两种都非常罕见.
当然,如果num是无符号类型,或者是C99固定宽度整数类型之一(int8_t等等,需要2的补码),那么这不是问题.在这种情况下,我认为%2是更优雅和有意义的,并且&1可能是有时可能需要表现的黑客.我认为例如CPython不进行这种优化,完全解释的语言也是如此(尽管解析开销可能使两个机器指令之间的差异相形见绌).虽然碰到C或C++编译器并没有尽可能地做到这一点,但我会感到有点惊讶,因为如果不是之前发布指令,它就是一个明智的选择.
一般来说,我会说在C++中你完全受编译器优化能力的支配.标准容器和算法具有n级间接,其中大部分在编译器完成内联和优化后消失.一个不错的C++编译器可以在早餐之前处理具有常量值的算术,并且不管你做什么,不合适的C++编译器都会产生垃圾代码.
Dou*_* T. 12
我定义并使用"IsEven"函数,所以我不必考虑它,然后我选择了一种方法或另一种方法而忘记了如何检查是否有问题.
只有nitpick/caveat我只是说通过按位操作,你假设一些二进制数字的表示,模数你不是.您将数字解释为十进制值.这几乎可以保证与整数一起使用.但是请考虑模数对于double是有效的,但是按位运算不会.
AnT*_*AnT 12
关于性能的结论是基于流行的错误前提.
出于某种原因,您坚持将语言操作转换为"明显的"机器对应,并根据该翻译得出性能结论.在这种特殊情况下,您得出结论,&C++语言的按位和操作必须通过按位和机器操作来实现,而模%运算必须以某种方式涉及机器分割,据称速度较慢.如果有的话,这种方法的用途非常有限.
首先,我无法想象一个真实的C++编译器会解释这样的"文字"的方式的语言操作,将其映射到"等效"机器操作IE浏览器.大多数情况下,你认为等效的机器操作根本就不存在.
当谈到这样的基本操作与一个立即数作为操作数,任何自我尊重编译器总是立即"理解",无论num & 1和num % 2对整体num做同样的事情,这将使编译器生成的两个表达式完全相同的代码.不用说,性能将完全相同.
顺便说一句,这不称为"优化".根据定义,优化是指编译器决定偏离抽象C++机器的标准行为以生成更有效的代码(保留程序的可观察行为).在这种情况下没有偏差,这意味着没有优化.
而且,很可能在给定的机器上实现两者的最佳方式既不是按位也不是除法,而是一些其他专用的机器专用指令.最重要的是,它很可能有不会有任何需要任何指令可言,因为即使岬/一特定值的奇岬可能通过处理器状态标志或者类似的东西暴露了"自由"那.
换句话说,效率参数无效.
其次,要回到原来的问题,更可取的方法来确定一个值的偶数内斯/奇岬肯定是num % 2方法,因为它("定义")的字面实现所需的检查,并明确表达了这一事实这张支票纯粹是数学的.即它清楚地表明我们关心数字的属性,而不是关于其表示的属性(如num & 1变体的情况).
该num & 1变种应情况下,当你想获得多项的值表示的位保留.使用此代码进行偶数/奇数检查是一个非常值得怀疑的做法.
已经多次提到任何现代编译器都会为这两个选项创建相同的程序集.这让我想起了前几天我在某处看到的LLVM演示页面,所以我想我会试一试.我知道这不仅仅是轶事,但它确实证实了我们的期望:x%2并且x&1实现相同.
我也尝试用gcc-4.2.1(gcc -S foo.c)编译这两个,并且结果汇编是相同的(并粘贴在这个答案的底部).
编程第一:
int main(int argc, char **argv) {
return (argc%2==0) ? 0 : 1;
}
Run Code Online (Sandbox Code Playgroud)
结果:
; ModuleID = '/tmp/webcompile/_27244_0.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
target triple = "i386-pc-linux-gnu"
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind readnone {
entry:
%0 = and i32 %argc, 1 ; <i32> [#uses=1]
ret i32 %0
}
Run Code Online (Sandbox Code Playgroud)
编程第二个:
int main(int argc, char **argv) {
return ((argc&1)==0) ? 0 : 1;
}
Run Code Online (Sandbox Code Playgroud)
结果:
; ModuleID = '/tmp/webcompile/_27375_0.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
target triple = "i386-pc-linux-gnu"
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind readnone {
entry:
%0 = and i32 %argc, 1 ; <i32> [#uses=1]
ret i32 %0
}
Run Code Online (Sandbox Code Playgroud)
GCC输出:
.text
.globl _main
_main:
LFB2:
pushq %rbp
LCFI0:
movq %rsp, %rbp
LCFI1:
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl -4(%rbp), %eax
andl $1, %eax
testl %eax, %eax
setne %al
movzbl %al, %eax
leave
ret
LFE2:
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
.set L$set$0,LECIE1-LSCIE1
.long L$set$0
LSCIE1:
.long 0x0
.byte 0x1
.ascii "zR\0"
.byte 0x1
.byte 0x78
.byte 0x10
.byte 0x1
.byte 0x10
.byte 0xc
.byte 0x7
.byte 0x8
.byte 0x90
.byte 0x1
.align 3
LECIE1:
.globl _main.eh
_main.eh:
LSFDE1:
.set L$set$1,LEFDE1-LASFDE1
.long L$set$1
ASFDE1:
.long LASFDE1-EH_frame1
.quad LFB2-.
.set L$set$2,LFE2-LFB2
.quad L$set$2
.byte 0x0
.byte 0x4
.set L$set$3,LCFI0-LFB2
.long L$set$3
.byte 0xe
.byte 0x10
.byte 0x86
.byte 0x2
.byte 0x4
.set L$set$4,LCFI1-LCFI0
.long L$set$4
.byte 0xd
.byte 0x6
.align 3
LEFDE1:
.subsections_via_symbols
Run Code Online (Sandbox Code Playgroud)
这一切都取决于背景.如果它是一个低级别的系统环境,我实际上更喜欢&1方法.在许多这样的情境中,"甚至"基本上意味着对我来说零位为零,而不是被两个整除.
但是:你的一个班轮有一个错误.
你必须去
if( (x&1) == 0 )
Run Code Online (Sandbox Code Playgroud)
不
if( x&1 == 0 )
Run Code Online (Sandbox Code Playgroud)
后者ANDs x与1 == 0,即ANDs x为0,产生0,当然总是评估为false.
所以,如果你完全按照你的建议去做,那么所有的数字都很奇怪!
| 归档时间: |
|
| 查看次数: |
17300 次 |
| 最近记录: |