否定数字的最快方法

问题描述 投票:35回答:7

我今天早上在想,将正数从负数反转为正,从负数到正数的最快方法是,当然,最简单的方法可能是:

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

int a = 10;
a = -a;

但是,我想,我要使用命令shift和指针来做到这一点...使用命令移位运算符和内存确实可以改变一个值的符号吗?

c++ c intel visual-c++-2012
7个回答
24
投票

第一个产生:

    .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

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

--------------------------编辑,运算符使用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

42
投票

使用可读的东西,例如

a *= -1;

a = -a;

将其余部分留给优化器。


9
投票

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

  • 您应该忘记速度,并选择最容易理解的习惯用法。
  • 几乎所有编译器(启用了优化功能)都会为a = -aa *= -1等之类的东西生成等效的最佳代码(可能是一条指令)。1
  • 任何试图使其更快的尝试都会使它的可读性大大降低,并且很容易使其变慢。
  • 如果需要优化,则应首先分析生成的代码和性能。

但是,*= -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;
[一旦采用了习惯用法,在其他情况下,人们往往会坚持下去。

1 [[Peter Cordes的观察]:几乎所有编译器都知道a = -aa *= -1完全相同,并且会发出他们认为对目标CPU最有效的asm。 ,无论您如何编写。 (例如,用于x86 gcc / MSVC / clang和ARM gcc的Godbolt compiler explorer。)>但是,尽管MSVS 2012(仅在调试模式下)每个使用一条指令,但在最近的Intel CPU上,它们使用实际的= -a指令对*= -1花费1个周期,对imul花费3个周期。

[假设处理器至少具有一定的能力并且具有sizeof(int) == sizeof(Cpu_register),那么“使该数字为负”将是一条指令(通常称为neg)[嗯,也可能需要加载和存储该值,但是如果您正在将该变量用于其他任何用途,它可以在加载后保留,并且仅在以后存储...]
-1的乘积最有可能比a = -a;慢,但是大多数称职的编译器应该能够使这两个等同。

因此,只需清楚地编写代码,其余的都应该照顾好自己。在大多数处理器中,取数字不是难事。如果您使用的是一些不寻常的处理器,请查看编译器的输出,然后查看其作用。

也为0-n
[Gcc在所有四种情况下均发出“ neg”指令:-n,0-n,n * -1和〜n + 1

使用高级语言的解决方案
诸如此类的问题在采访和竞争性编程世界中很流行。

我登陆这里研究了更多的否定数字解决方案,而无需使用-或+运算符。

为此:

使用〜运算符补码

    然后使用Half加法器逻辑在步骤1中获得的数字加1:
  1. > int addNumbers(int x, int y) > { > if(y==0) return x; // carry is 0 > return addNumbers(x^y,(x&y)<<1); > }
这里x ^ y执行位加法,x&y句柄执行操作

您可以尝试
int a = 10; a= ~a+1;

但是您不必担心,因为编译器以最佳方式实现了它。


3
投票

3
投票

1
投票
使用高级语言的解决方案

-2
投票
© www.soinside.com 2019 - 2024. All rights reserved.