-1
的乘积最有可能比a = -a;
慢,但是大多数称职的编译器应该能够使这两个等同。 我今天早上在想,将正数从负数反转为正,从负数到正数的最快方法是,当然,最简单的方法可能是:
int a = 10;
a = a*(-1);
或
int a = 10;
a = -a;
但是,我想,我要使用命令shift和指针来做到这一点...使用命令移位运算符和内存确实可以改变一个值的符号吗?
第一个产生:
.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
使用可读的东西,例如
a *= -1;
或
a = -a;
将其余部分留给优化器。
其他答案已正确表明可读性更重要:
a = -a
,a *= -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 = -a
和a *= -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:
> 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;
但是您不必担心,因为编译器以最佳方式实现了它。