Question

I was thinking this morning here, what would be the fastest way to reverse a number of positive to negative and from negative to positive, of course, the simplest way might be:

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

or

int a = 10;
a = -a;

But then, I thought, I take that to do this, using commands shift and pointers ... That really would be possible to change the sign of a value, using commands shift operators and memory?

Was it helpful?

Solution 2

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.

OTHER TIPS

Use something that is readable, such as

a *= -1;

or

a = -a;

Leave the rest to the optimizer.

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.


1 Observations 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.

Also 0 - n

Gcc emits the "neg" instruction for all four cases: -n, 0 - n, n * -1, and ~n + 1

Assuming the processor is at least somewhat competent and has sizeof(int) == sizeof(Cpu_register), then a "make this number negative" will be a single instruction (usually called neg) [well, may need the value loading and storing too, but if you are using the variable for anything else, it can remain after the load, and only be stored later one...]

Multiplying by -1 is most likely slower than a = -a;, but most competent compilers should be able to make both of these equivalent.

So, just write the code clearly, and the rest should take care of itself. Negating a number is not a difficult operation in most processors. If you are using some unusual processsor, then look at the compiler output, and see what it does.

Solution using high level language

Questions like these are popular in interviews and competitive programming world .

I landed here researching more solution for negation of a number without using - or + operator .

For this :

  1. complement a number using ~ operator
  2. Then add 1 to the number obtained in step 1 using Half adder logic :
> int addNumbers(int x, int y)
>       {
>                    if(y==0)  return x; // carry is 0 
>                    return addNumbers(x^y,(x&y)<<1);
>         }

Here x^y performs addition of bits and x&y handles carry operation

You can try

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

but you shouldn't worry about that, because compiler makes it in the best way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top