web-dev-qa-db-fra.com

moyen le plus rapide de nier un nombre

Je pensais ce matin ici, quel serait le moyen le plus rapide d’inverser un certain nombre de points positifs à négatifs et de négatifs à positifs, bien sûr, le moyen le plus simple pourrait être:

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

ou

int a = 10;
a = -a;

Mais alors, pensai-je, je prends cela pour cela, en utilisant des commandes shift et des pointeurs .... Cela serait vraiment possible de changer le signe d’une valeur en utilisant des commandes shift opérateurs et de la mémoire?

31
Alexandre

Le premier produit:

    .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

Le second produit:

    .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

Même résultat! Aucune différence dans le code d'assemblage produit.

-------------------------- EDIT, OP REPONSES IL UTILISE VC++ 2012, INTEL Arch ------------ --------

Compilé avec 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

et avec une seconde approche (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
22
Aniket Inge

Utilisez quelque chose qui est lisible, tel que

a *= -1;

ou

a = -a;

Laissez le reste à l'optimiseur.

37
Armen Tsirunyan

Les autres réponses ont correctement indiqué que la lisibilité importait davantage:

  • Vous devriez oublier la vitesse et choisir l’idiome que vous trouvez le plus lisible.
  • Presque tous les compilateurs (avec les optimisations activées) comprennent que a = -a et a *= -1 sont exactement identiques et émettront quel que soit leur choix, ce qui sera le plus efficace pour le processeur cible, quelle que soit la manière dont vous l'écrivez. (par exemple, Explorateur du compilateur Godbolt pour x86 gcc/MSVC/clang et ARM gcc.)
    • Toutefois, bien que MSVS 2012 (en mode débogage uniquement) utilise une instruction pour chaque instruction, il faut un cycle pour = -a et 3 pour *= -1 sur les processeurs Intel récents, en utilisant une instruction imul réelle.
  • Toute tentative pour le rendre plus rapide le rendra beaucoup moins lisible et pourrait facilement le ralentir.
  • Si vous avez besoin d'optimiser, commencez par analyser le code généré et les performances.


Il existe cependant un avantage pratique par rapport à l'idiome *= -1: vous n'avez qu'à écrire le côté gauche une fois, il n'est évalué qu'une fois - et le lecteur ne doit le lire qu'une fois! Ceci est pertinent lorsque le LHS est long, complexe, coûteux ou peut avoir des effets secondaires:

(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;

Et une fois que l’on a adopté un idiome, on a tendance à le conserver dans d’autres situations.

6
PJTraill

Aussi 0 - n

Gcc émet l'instruction "neg" pour les quatre cas: -n, 0 - n, n * -1 et ~ n + 1

3
Jeremy

En supposant que le processeur soit au moins assez compétent et qu'il ait sizeof(int) == sizeof(Cpu_register), alors "rendre ce nombre négatif" sera une seule instruction (généralement appelée neg) [ainsi, il faudra peut-être charger et stocker également la valeur, quoi que ce soit d'autre, il peut rester après le chargement, et ne sera stocké que plus tard ...]

Multiplier par -1 est probablement plus lent que a = -a;, mais la plupart des compilateurs compétents devraient être capables de rendre ces deux équivalents. 

Donc, écrivez simplement le code, et le reste devrait prendre soin de lui-même. La négation d’un nombre n’est pas une opération difficile pour la plupart des processeurs. Si vous utilisez un processeur inhabituel, examinez la sortie du compilateur et voyez ce qu’elle fait.

3
Mats Petersson

Solution utilisant un langage de haut niveau

De telles questions sont populaires dans les interviews et dans le monde de la programmation compétitive.

J'ai atterri ici à la recherche de plus de solution pour la négation d'un nombre sans utiliser l'opérateur - ou +. 

Pour ça :

  1. compléter un nombre en utilisant ~ opérateur 
  2. Ajoutez ensuite 1 au nombre obtenu à l'étape 1 à l'aide de la logique Half adder:
> int addNumbers(int x, int y)
>       {
>                    if(y==0)  return x; // carry is 0 
>                    return addNumbers(x^y,(x&y)<<1);
>         }

Ici, x ^ y ajoute des bits et les poignées x & y 

1
Divyanshu Jimmy