C++ 否定一个数字的最快方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15111466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:02:29  来源:igfitidea点击:

fastest way to negate a number

c++visual-c++x86micro-optimizationvisual-c++-2012

提问by Alexandre

I was thinking this morning here, what would be the fastest way to reverse a number of positive to negative and from negative to positive, of course, the simplest way might be:

我今天早上在这里想,将一些正面反转为负面和从负面反转为正面的最快方法是什么,当然,最简单的方法可能是:

int a = 10;
a = a*(-1);

or

或者

int a = 10;
a = -a;

But then, I thought, I take that to do this, using commands shift and pointers ... That really would be possible to change the sign of a value, using commands shift operators and memory?

但是,然后,我想,我会这样做,使用命令移位和指针......使用命令移位运算符和内存真的可以改变值的符号吗?

采纳答案by Aniket Inge

With optimization disabled, gcc for x86 compiles the first to this asm:

禁用优化后,x86 的 gcc 将第一个编译为这个 asm:

    .file   "optimum.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    , %esp
    call    ___main               # MinGW library init function
    movl    , 12(%esp) ;i = 10
    negl    12(%esp)      ;i = -i
    movl    
    .file   "optimum.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    , %esp
    call    ___main
    movl    , 12(%esp)   ;i = 10
    negl    12(%esp)        ;i = -i
    movl    
; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 

    TITLE   C:\Users\Dell\Downloads\TTH\TTH\TTH\optimum.c
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  _main
; Function compile flags: /Odtp
_TEXT   SEGMENT
_a$ = -4                        ; size = 4
_argc$ = 8                      ; size = 4
_argv$ = 12                     ; size = 4
_main   PROC
; File c:\users\dell\downloads\tth\tth\tth\optimum.c
; Line 4
    push    ebp
    mov ebp, esp
    push    ecx
; Line 5
    mov DWORD PTR _a$[ebp], 10          ; 0000000aH
; Line 6
    mov eax, DWORD PTR _a$[ebp]
    neg eax ;1 machine cycle!
    mov DWORD PTR _a$[ebp], eax
; Line 7
    xor eax, eax
; Line 8
    mov esp, ebp
    pop ebp
    ret 0
_main   ENDP
_TEXT   ENDS
END
, %eax leave ret
, %eax leave ret

With optimization disabled, the second one produces:

禁用优化后,第二个产生:

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01 

    TITLE   C:\Users\Dell\Downloads\TTH\TTH\TTH\optimum.c
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  _main
; Function compile flags: /Odtp
_TEXT   SEGMENT
_a$ = -4                        ; size = 4
_argc$ = 8                      ; size = 4
_argv$ = 12                     ; size = 4
_main   PROC
; File c:\users\dell\downloads\tth\tth\tth\optimum.c
; Line 4
    push    ebp
    mov ebp, esp
    push    ecx
; Line 5
    mov DWORD PTR _a$[ebp], 10          ; 0000000aH
; Line 6
    mov eax, DWORD PTR _a$[ebp]
    imul    eax, -1 ;1 instruction, 3 machine/cycles :|
    mov DWORD PTR _a$[ebp], eax
; Line 7
    xor eax, eax
; Line 8
    mov esp, ebp
    pop ebp
    ret 0
_main   ENDP
_TEXT   ENDS
END

Same output! No difference in the assembly code produced.

一样的输出!生成的汇编代码没有区别。

--------------------------EDIT, OP ANSWERS HE USES VC++2012, INTEL ARCH-------------------

--------------------------编辑,OP 答案他使用 VC++2012,INTEL ARCH ----------- --------

Compiled using cl optimum.c /Fa optimum.asm(optimization disabled)

编译使用cl optimum.c /Fa optimum.asm(禁用优化)

a *= -1;

and with second approach (a = a * -1), optimization disabled MSVC:

使用第二种方法 ( a = a * -1),优化禁用了 MSVC:

a = -a;

So if you care about the performance of your debug-mode asm under MSVC, you could optimize your source accordingly. Normally you only care about performance in optimized builds.

因此,如果您关心 MSVC 下调试模式 asm 的性能,则可以相应地优化源代码。通常,您只关心优化构建中的性能。

回答by Armen Tsirunyan

Use something that is readable, such as

使用可读的东西,例如

(valid ? a : b)[prime_after(i++)] *= -1;
*look_up (input) *= -1;  // Where look_up may have side-effects
parity[state][(unsigned int)getc(stdin)] *= -1;
variable_with_a_long_explanatory_name *= -1;

or

或者

> int addNumbers(int x, int y)
>       {
>                    if(y==0)  return x; // carry is 0 
>                    return addNumbers(x^y,(x&y)<<1);
>         }

Leave the rest to the optimizer.

剩下的交给优化器。

回答by PJTraill

The other answers have correctly indicated that readability matters more:

其他答案正确地表明可读性更重要:

  • You should forget about speed and choose the idiom that you find most readable.
  • Almost all compilers (with optimizations enabled) generate equivalent optimal code (probably a single instruction) for anything like a = -a, a *= -1etc.1
  • Any attempt to make it faster will make it far less readable and could easily make it slower.
  • If you need to optimise, you should start by analysing generated code and performance.
  • 您应该忘记速度并选择您认为最易读的习语。
  • 几乎所有的编译器(启用了优化)产生像任何等价最佳代码(可能是一个单指令)a = -aa *= -11
  • 任何使其更快的尝试都会使其可读性大大降低,并且很容易使其变慢。
  • 如果你需要优化,你应该从分析生成的代码和性能开始。


There is however a practical advantageto the *= -1idiom: you only have to write the left hand side once, it is only evaluated once – and the reader only has to read it once! This is relevant when the LHS is long, complex or expensive or may have side-effects:


然而,这个习语有一个实际的优势*= -1:你只需要写一次左边,它只被评估一次——而读者只需要阅读一次!当 LHS 时间长、复杂或昂贵或可能有副作用时,这是相关的:

> int addNumbers(int x, int y)
>       {
>                    if(y==0)  return x; // carry is 0 
>                    return addNumbers(x^y,(x&y)<<1);
>         }

And once one has adopted an idiom, one tends to stick with it in other situations.

一旦采用了一种习语,人们往往会在其他情况下坚持使用它。



1Observations by Peter Cordes: Almost all compilers understand that a = -aand a *= -1are exactly the same and will emit whatever asm they decide will be most efficient on the target CPU, regardless of how you write it. (e.g. Godbolt compiler explorerfor x86 gcc/MSVC/clang, and ARM gcc.)But though MSVS 2012 (in debug mode only) uses one instruction for each, they take 1 cycle for = -aand 3 for *= -1on recent Intel CPUs, using an actual imulinstruction.

1 Peter Cordes 的观察:几乎所有的编译器都理解这一点a = -aa *= -1并且完全相同,并且会发出他们认为在目标 CPU 上最有效的任何 asm,无论您如何编写它。(例如,用于 x86 gcc/MSVC/clang 和 ARM gcc 的Godbolt 编译器资源管理器。)但是,尽管 MSVS 2012(仅在调试模式下)为每个指令使用一条指令,但它们在最近的 Intel CPU 上需要 1 个周期= -a和 3个周期*= -1,使用实际imul操作说明。

回答by Mats Petersson

Assuming the processor is at least somewhat competent and has sizeof(int) == sizeof(Cpu_register), then a "make this number negative" will be a single instruction (usually called neg) [well, may need the value loading and storing too, but if you are using the variable for anything else, it can remain after the load, and only be stored later one...]

假设处理器至少在某种程度上有能力并且有sizeof(int) == sizeof(Cpu_register),那么“使这个数字为负”将是一条指令(通常称为neg)[好吧,可能也需要加载和存储值,但如果您将变量用于其他任何事情,它可以在加载后保留,并且只能在以后存储一个...]

Multiplying by -1is most likely slower than a = -a;, but most competent compilers should be able to make both of these equivalent.

乘法-1很可能比 慢a = -a;,但大多数有能力的编译器应该能够使这两者等效。

So, just write the code clearly, and the rest should take care of itself. Negating a number is not a difficult operation in most processors. If you are using some unusual processsor, then look at the compiler output, and see what it does.

所以,只要把代码写清楚,剩下的就自己处理好了。在大多数处理器中,否定一个数字并不是一个困难的操作。如果您正在使用一些不寻常的处理器,请查看编译器输出,看看它做了什么。

回答by Jeremy

Also 0 - n

还有 0 - n

Gcc emits the "neg" instruction for all four cases: -n, 0 - n, n * -1, and ~n + 1

Gcc 为所有四种情况发出“neg”指令:-n、0 - n、n * -1 和 ~n + 1

回答by Divyanshu Jimmy

Solution using high level language

使用高级语言的解决方案

Questions like these are popular in interviews and competitive programming world .

像这样的问题在面试和竞争性编程世界中很流行。

I landed here researching more solution for negation of a number without using - or + operator .

我来到这里研究更多不使用 - 或 + 运算符的数字求反的解决方案。

For this :

为了这 :

  1. complement a number using ~ operator
  2. Then add 1 to the number obtained in step 1 using Half adder logic :
  1. 使用 ~ 运算符补充一个数字
  2. 然后使用半加法器逻辑将步骤 1 中获得的数字加 1:
int a = 10;
a= ~a+1;
##代码##

Here x^y performs addition of bits and x&y handles carry operation

这里 x^y 执行位相加,x&y 处理进位操作

回答by Alexander

You can try

你可以试试

##代码##

but you shouldn't worry about that, because compiler makes it in the best way.

但是您不必担心,因为编译器以最好的方式做到了这一点。