45

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?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Alexandre
  • 1,985
  • 5
  • 30
  • 55
  • 25
    The compiler will probably optimize such simple things. Try for yourself. – Zeta Feb 27 '13 at 11:54
  • 4
    agree with Zeta. Go for what is most readable – default Feb 27 '13 at 11:55
  • 1
    Not sure what you want to do with "pointers" and why you think using them would be faster than `a = -a;`. In fact, I don't understand why you think the compiler would not necessarily use the most efficient implementation when compiling `a = -a;`. – Mr Lister Feb 27 '13 at 11:57
  • 2
    Rule 1 of compiled languages: don't lie to the compiler; it'll come back and bite you later. If you find the compiler does a poor job then submit a bug report to the developers. Optimize your algorithms, not your basic operations. – ams Feb 27 '13 at 12:08
  • @Alexandre which computer architecture are you using? which compiler? – Aniket Inge Feb 27 '13 at 12:09
  • It's not that, is just a doubts... – Alexandre Feb 27 '13 at 11:56
  • I do not understand why this post was downvoted, I suggested the best possible solution to this problem -- using twos complement in assembly, or using an inbuilt assembly command to negate, and link it with C code. – Dmytro Feb 27 '13 at 11:58
  • 1
    Unless the assembler is placed `inline` and definitely not otherwise affecting the code-generation, it's unlikely to produce better code than the compiler... – Mats Petersson Feb 27 '13 at 12:02
  • 1
    @Dmitry it could be worse dependent on the context of the negation. in a more complex example, the compiler might decide not to negate at all. – Andreas Grapentin Feb 27 '13 at 12:06
  • Why go to the hassle of doing this in assembly language? Makes the code less readable (maintainable). The compiler will perform the necessary optimizations and will do a good job. If you are worried about this for optimization you would be more fruitful looking at the algorithm as a whole. IMHO - Micro-optimizations of this sort enable very little in savings and are a waste of time. – Ed Heal Feb 27 '13 at 14:07
  • @EdHeal, see ams' Rule #1. If you start writing "clever" code, the compiler can't use its patterns for straightforward code, and will probably generate worse (or even incorrect) code. Plus _you_ won't understand it a week later. It often means _worse_ performance. – vonbrand Feb 27 '13 at 16:57
  • If you're using a compiler that doesn't emit the best possible code for `a = -a;` and `a *= -1;`, use a better compiler. (Or enable the correct options for your compiler, e.g. `gcc -O3 -march=native`.) – Peter Cordes Nov 09 '17 at 04:03
  • @Dmitry: using inline assembly can *easily* be worse; it defeats constant-propagation (which can happen after inlining in places you weren't expecting), and it prevents the compiler from understanding what's going on which defeats other optimizations. e.g. if you use a `neg` instruction in inline asm, it can't fold into later code that adds the negated value to something else. If you'd used `a *= -1;`, the compiler could just `sub` instead of `neg` / `add`. See https://gcc.gnu.org/wiki/DontUseInlineAsm. – Peter Cordes Nov 09 '17 at 04:13

7 Answers7

51

Use something that is readable, such as

a *= -1;

or

a = -a;

Leave the rest to the optimizer.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
27

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Aniket Inge
  • 25,375
  • 5
  • 50
  • 78
  • 1
    `gcc` with no optimizations! `gcc -c optimum.c -S -o optimum.s` – Aniket Inge Feb 27 '13 at 12:04
  • Stupid question, but how do you know which compiler the OP uses? – Mr Lister Feb 27 '13 at 12:05
  • 7
    @MrLister any decent self-respecting compiler will produce the same(similar) code. – Aniket Inge Feb 27 '13 at 12:06
  • And how do you know what CPU the OP uses? – Lundin Feb 27 '13 at 12:08
  • That was my point - we don't know ANY of those things. But I would still expect the compiler to do a fair job, whatever compiler/processor it is. – Mats Petersson Feb 27 '13 at 12:09
  • OP asks lots of questions about `C#`, `winapi`, `winforms`. – Peter Wood Feb 27 '13 at 12:16
  • My guess, OP uses visual studio, x86 or x64(I don't have x64 yet) @PeterWood – Aniket Inge Feb 27 '13 at 12:17
  • 2
    The point is: don't guess, don't assume, don't read minds :) The question was about C or C++ in general. But indeed it doesn't make any sense at all to attempt manual optimizations unless you have in-depth knowledge of the particular CPU. 99% of all programmers do not have better knowledge of such things than the person who wrote the compiler port for the specific CPU. – Lundin Feb 27 '13 at 12:26
  • I think good answer. What ever compiler OP got he can try with same approach. Additionally if OP bothers to know which faster between `a = a*(-1);` and `a = -a;` then he need to(has to) understand low level stuff like this. So I think best answer. Good Aniket!! *Only lack of comments in asm code* – Grijesh Chauhan Feb 27 '13 at 12:29
  • @Aniket added little comment i feel will be helpful for OP. – Grijesh Chauhan Feb 27 '13 at 12:36
  • @GrijeshChauhan ah, re-add it man, I was editing at that time too I think.. I just edited my answer to include output for VC++2010 – Aniket Inge Feb 27 '13 at 12:42
  • 1
    @Aniket You should be able to see his edit in the edit history. It was `movl $10, 12(%esp) // i = 10` and `negl 12(%esp) // i = -i` respectively. @GrijeshChauhan it is considered better to suggest such changes to the poster through comments rather than to edit the post yourself, as you would be changing the meaning of the contents. – Lundin Feb 27 '13 at 12:46
  • 1
    @Aniket: Were the MSVS 2012 compilations optimised? Perhaps you could indicate this – perhaps /Odtp means that, but it is not obvious! It might also be kind to present only the differences in the listings. – PJTraill May 27 '15 at 15:20
  • @PJTraill: Obviously all of these compiler outputs are with optimizations disabled. (And would optimize away to nothing with optimizations because of a poor choice of test-case.) MSVC is really really stupid with optimizations disabled, it doesn't even use a memory operand with `imul` to save doing a separate `mov` load. (i.e. `imul eax, DWORD PTR _a$[ebp], -1`). I put `int mul(int a) { return a * -1; }` [on Godbolt so we can see the asm with gcc/clang/MSVC](https://godbolt.org/g/MmvN1f). It compiles the same as unary `-` with every compiler with optimization enabled, including non-x86. – Peter Cordes Nov 09 '17 at 03:44
12

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.

PJTraill
  • 1,353
  • 12
  • 30
5

Also 0 - n

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

Jeremy
  • 304
  • 1
  • 6
4

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.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
1

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

Divyanshu Jimmy
  • 2,542
  • 5
  • 32
  • 48
0

You can try

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

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

Alexander
  • 43
  • 5
  • 2
    That would be an illustration of precisely why this sort of optimization is a bad idea. As written, it is both more operations and less readable. A decent compiler will help you with the first issue, but not the second one. – rici Feb 27 '13 at 13:18
  • 2
    @rici and non-portable, as it relies on two's complement. – Gauthier May 27 '15 at 15:08
  • @rici: It's more operations in the C abstract machine, but it still compiles to a `neg` with gcc/clang/MSVC https://godbolt.org/g/C3F3Qx (on 2's complement machines like x86 and ARM which provide these operations in one instruction). If you do it with `unsigned` (and only convert back to `int` after the bit manipulation), it has the interesting advantage of not being UB on INT_MIN, while `-a` or `a * -1` would be signed overflow. That is the *only* advantage though. It's horrible for readability, and not portable. – Peter Cordes Nov 09 '17 at 03:59
  • 1
    @peter: although i wrote that comment four and a half years ago, i think can stand by it. I did say that a decent compiler would optimize the operations, and so the fact that gcc, clang, etc. do so is entirely consistent with what I wrote. Why do you claim that `-a` is UB on *unsigned* values? – rici Nov 09 '17 at 05:15
  • @rici: I claimed that for `int a = INT_MIN`, it's UB to do `-a`, but not to do `(int)(~(unsigned)a + 1)`. Of course, now that I think about it, you could just do `(int)-(unsigned)a` to get the same result (the two's complement/unsigned negation, even on one's complement or sign/magnitude C++ implementations). So nvm, this has zero advantages. (Re: efficiency: I only said that this was not worse. Definitely not that it was better! As you say, it compiles the same as `-` and `*= -1`.) – Peter Cordes Nov 09 '17 at 05:26