fastest way to negate a number
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 *= -1
etc.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.
There is however a practical advantage to the *= -1
idiom: 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:
(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;
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 = -a
and a *= -1
are 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 explorer for 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 = -a
and 3 for *= -1
on recent Intel CPUs, using an actual imul
instruction.
Use something that is readable, such as
a *= -1;
or
a = -a;
Leave the rest to the optimizer.
With optimization disabled, gcc for x86 compiles the first to this 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
With optimization disabled, the second one produces:
.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
Same output! No difference in the assembly code produced.
--------------------------EDIT, OP ANSWERS HE USES VC++2012, INTEL ARCH-------------------
Compiled using cl optimum.c /Fa optimum.asm
(optimization disabled)
; 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
and with second approach (a = a * -1
), optimization disabled 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
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.
Also 0 - n
Gcc emits the "neg" instruction for all four cases: -n, 0 - n, n * -1, and ~n + 1