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?
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
Utilisez quelque chose qui est lisible, tel que
a *= -1;
ou
a = -a;
Laissez le reste à l'optimiseur.
Les autres réponses ont correctement indiqué que la lisibilité importait davantage:
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.) = -a
et 3 pour *= -1
sur les processeurs Intel récents, en utilisant une instruction imul
réelle.
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.
Aussi 0 - n
Gcc émet l'instruction "neg" pour les quatre cas: -n, 0 - n, n * -1 et ~ n + 1
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.
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 :
> 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