Boolean multiplication in c++?

让人想犯罪 __ 提交于 2019-12-23 07:00:39

问题


Consider the following:

inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;}
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;}

The syntax of f2 is more compact, but do the standard guarantees that f1 and f2 are strictly equivalent ?

Furthermore, if I want the compiler to optimize this expression if b and i are known at compile-time, which version should I prefer ?


回答1:


Well, yes, both are equivalent. bool is an integral type and true is guaranteed to convert to 1 in integer context, while false is guaranteed to convert to 0.

(The reverse is also true, i.e. non-zero integer values are guaranteed to convert to true in boolean context, while zero integer values are guaranteed to convert to false in boolean context.)

Since you are working with unsigned types, one can easily come up with other, possibly bit-hack-based yet perfectly portable implementations of the same thing, like

i & -(unsigned) b

although a decent compiler should be able to choose the best implementation by itself for any of your versions.

P.S. Although to my great surprise, GCC 4.1.2 compiled all three variants virtually literally, i.e. it used machine multiplication instruction in multiplication-based variant. It was smart enough to use cmovne instruction on the ?: variant to make it branchless, which quite possibly made it the most efficient implementation.




回答2:


Yes. It's safe to assume true is 1 and false is 0 when used in expressions as you do and is guaranteed:

C++11, Integral Promotions, 4.5:

An rvalue of type bool can be converted to an rvalue of type int, with false becoming zero and true becoming one.




回答3:


The compiler will use implicit conversion to make an unsigned int from b, so, yes, this should work. You're skipping the condition checking by simple multiplication. Which one is more effective/faster? Don't know. A good compiler would most likely optimize both versions I'd assume.




回答4:


FWIW, the following code

inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;}
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;}

int main()
{
    volatile unsigned int i = f1(42, true);
    volatile unsigned int j = f2(42, true);
}

compiled with gcc -O2 produces this assembly:

    .file   "test.cpp"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section    .text.startup,"x"
    .p2align 2,,3
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB2:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp
    call    ___main
    movl    $42, 8(%esp)   // i
    movl    $42, 12(%esp)  // j
    xorl    %eax, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE2:

There's not much left of either f1 or f2, as you can see.

As far as C++ standard is concerned, the compiler is allowed to do anything with regards to optimization, as long as it doesn't change the observable behaviour (the as if rule).



来源:https://stackoverflow.com/questions/14034478/boolean-multiplication-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!