否定数字的最快方法

时间:2013-02-27 11:54:01

标签: c++ c intel visual-c++-2012

我今天早上在这里思考,最好的方法是将一些积极转为负面,从消极转为正面,当然,最简单的方法可能是:

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

int a = 10;
a = -a;

然而,我想,我接着这样做,使用命令shift和指针...... 真的可以使用命令移位运算符和内存来改变值的符号吗?

7 个答案:

答案 0 :(得分:37)

使用可读的内容,例如

a *= -1;

a = -a;

将其余部分留给优化器。

答案 1 :(得分:22)

第一个产生:

    .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    $16, %esp
    call    ___main
    movl    $10, 12(%esp) ;i = 10
    negl    12(%esp)      ;i = -i
    movl    $0, %eax
    leave
    ret

第二个产生:

    .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    $16, %esp
    call    ___main
    movl    $10, 12(%esp)   ;i = 10
    negl    12(%esp)        ;i = -i
    movl    $0, %eax
    leave
    ret

相同的输出!生成的汇编代码没有区别。

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

使用cl optimum.c /Fa optimum.asm

进行编译
; 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

并采用第二种方法(a = a * -1)

; 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

答案 2 :(得分:6)

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

  • 你应该忘记速度并选择你认为最具可读性的成语。
  • 几乎所有编译器(启用了优化)都明白a = -aa *= -1完全相同,并且会发出他们认为在目标CPU上最有效的任何asm,无论你如何编写它。 (例如Godbolt compiler explorer表示x86 gcc / MSVC / clang和ARM gcc。)
    • 但是虽然MSVS 2012(仅在调试模式下)每个都使用一条指令,但在最近的Intel CPU上,= -a*= -1需要1个周期,使用实际的imul指令。
  • 任何使其速度更快的尝试都会降低其可读性,并且很容易使其变慢。
  • 如果您需要优化,则应首先分析生成的代码和性能。


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

(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;

一旦采用了成语,人们就会在其他情况下坚持使用。

答案 3 :(得分:3)

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

乘以-1最有可能慢于a = -a;,但大多数称职的编制者应该能够使这两者等效。

所以,只要清楚地编写代码,其余的就应该自己处理。在大多数处理器中,否定数字并不困难。如果你正在使用一些不寻常的过程,那么看看编译器输出,看看它做了什么。

答案 4 :(得分:3)

也是0 - n

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

答案 5 :(得分:1)

  

使用高级语言的解决方案

这些问题在采访和竞争性编程领域很受欢迎。

我在这里找到了更多的解决方案来研究否定数字而不使用 - 或+运算符。

为此:

  1. 使用~operator
  2. 补充数字
  3. 然后使用半加法逻辑将步骤1中获得的数字加1:
  4. > int addNumbers(int x, int y)
    >       {
    >                    if(y==0)  return x; // carry is 0 
    >                    return addNumbers(x^y,(x&y)<<1);
    >         }
    

    这里x ^ y执行位的添加和x&amp; y句柄进位操作

答案 6 :(得分:-2)

你可以尝试

int a = 10;
a= ~a+1;

但你不应该担心,因为编译器以最好的方式使它。