我今天早上在这里想,将多个正面反转为负面以及从负面反转为正面的最快方法是什么,当然,最简单的方法可能是:
int a = 10;
a = a*(-1);
或者
int a = 10;
a = -a;
但后来,我想,我会这样做,使用命令移位和指针......这真的有可能改变一个值的符号,使用命令移位运算符和内存?
我今天早上在这里想,将多个正面反转为负面以及从负面反转为正面的最快方法是什么,当然,最简单的方法可能是:
int a = 10;
a = a*(-1);
或者
int a = 10;
a = -a;
但后来,我想,我会这样做,使用命令移位和指针......这真的有可能改变一个值的符号,使用命令移位运算符和内存?
使用可读的东西,例如
a *= -1;
或者
a = -a;
剩下的留给优化器。
在禁用优化的情况下,gcc for x86 将第一个编译成这个 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 $16, %esp
call ___main # MinGW library init function
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 的 OP ANSWERS,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
),优化禁用 MSVC:
; 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
因此,如果您关心 MSVC 下调试模式 asm 的性能,您可以相应地优化您的源代码。通常你只关心优化构建的性能。
其他答案正确地表明可读性更重要:
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 编译器资源管理器
。)但尽管 MSVS 2012(仅在调试模式下)对每个指令使用一条指令,但在最近的 Intel CPU 上,它们需要 1 个周期= -a
和 3个周期*= -1
,使用实际imul
操作说明。
也是 0 - n
Gcc 为所有四种情况发出“否定”指令:-n、0 - n、n * -1 和 ~n + 1
假设处理器至少有一定的能力并且具有sizeof(int) == sizeof(Cpu_register)
,那么“使这个数字为负”将是一条指令(通常称为neg
)[嗯,可能也需要加载和存储值,但是如果您将变量用于其他任何事情,它可以在加载后保留,并且只能在以后存储......]
乘以-1
很可能比 慢a = -a;
,但大多数有能力的编译器应该能够使这两者等效。
所以,只要把代码写清楚,剩下的就自己处理好了。在大多数处理器中,取反一个数字并不是什么困难的操作。如果你使用了一些不寻常的处理器,那么看看编译器的输出,看看它做了什么。
使用高级语言的解决方案
像这样的问题在面试和竞争激烈的编程世界中很流行。
我来到这里研究更多不使用 - 或 + 运算符的否定数字解决方案。
为了这 :
> 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;
但你不应该担心这一点,因为编译器以最好的方式使它成为可能。