mentre altri hanno spiegato quale sia la causa del problema è, credo che la soluzione "migliore" dovrebbe essere quello di scrivere il condizionale con se:
int x1 = 10, x2=20, y1=132, y2=12, minx, miny, maxx, maxy;
if (x1<=x2)
{
minx=x1;
maxx=x2;
}
else
{
minx=x2;
maxx=x1;
}
if (y1<=y2)
{
miny=y1;
maxy=y2;
}
else
{
miny=y2;
maxy=y1;
}
Sì, è più linee più a lungo, ma è anche più facile per leggere e chiarire esattamente cosa succede (e se hai bisogno di attraversarlo nel debugger, puoi facilmente vedere in che direzione va).
Qualsiasi compilatore moderno dovrebbe essere in grado di convertire uno di questi in assegnamenti condizionali piuttosto efficienti che faccia un buon lavoro di evitare rami (e quindi "previsione di ramo errato").
ho preparato un piccolo test, che ho compilato utilizzando
g++ -O2 -fno-inline -S -Wall ifs.cpp
Ecco la fonte (ho dovuto farlo parametri per garantire il compilatore non solo calcolare direttamente il valore corretto e solo fare mov $12,%rdx
, ma in realtà ha un confrontare e decidere con è più grande):
void mine(int x1, int x2, int y1, int y2)
{
int minx, miny, maxx, maxy;
if (x1<=x2)
{
minx=x1;
maxx=x2;
}
else
{
minx=x2;
maxx=x1;
}
if (y1<=y2)
{
miny=y1;
maxy=y2;
}
else
{
miny=y2;
maxy=y1;
}
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
void original(int x1, int x2, int y1, int y2)
{
int minx, miny, maxx, maxy;
x1<=x2 ? (minx=x1,maxx=x2) : (minx=x2,maxx=x1);
y1<=y2 ? (miny=y1,maxy=y2) : (miny=y2,maxy=y1);
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
void romano(int x1, int x2, int y1, int y2)
{
int minx, miny, maxx, maxy;
minx = ((x1 <= x2) ? x1 : x2);
maxx = ((x1 <= x2) ? x2 : x1);
miny = ((y1 <= y2) ? y1 : y2);
maxy = ((y1 <= y2) ? y2 : y1);
cout<<"minx="<<minx<<"\n";
cout<<"maxx="<<maxx<<"\n";
cout<<"miny="<<miny<<"\n";
cout<<"maxy="<<maxy<<"\n";
}
int main()
{
int x1=10, x2=20, y1=132, y2=12;
mine(x1, x2, y1, y2);
original(x1, x2, y1, y2);
romano(x1, x2, y1, y2);
return 0;
}
Il codice generato è simile al seguente:
_Z4mineiiii:
.LFB966:
.cfi_startproc
movq %rbx, -32(%rsp)
movq %rbp, -24(%rsp)
movl %ecx, %ebx
movq %r12, -16(%rsp)
movq %r13, -8(%rsp)
movl %esi, %r12d
subq $40, %rsp
movl %edi, %r13d
cmpl %esi, %edi
movl %edx, %ebp
cmovg %edi, %r12d
cmovg %esi, %r13d
movl $_ZSt4cout, %edi
cmpl %ecx, %edx
movl $.LC0, %esi
cmovg %edx, %ebx
cmovg %ecx, %ebp
.... removed actual printout code that is quite long and unwieldy...
_Z8originaliiii:
movq %rbx, -32(%rsp)
movq %rbp, -24(%rsp)
movl %ecx, %ebx
movq %r12, -16(%rsp)
movq %r13, -8(%rsp)
movl %esi, %r12d
subq $40, %rsp
movl %edi, %r13d
cmpl %esi, %edi
movl %edx, %ebp
cmovg %edi, %r12d
cmovg %esi, %r13d
movl $_ZSt4cout, %edi
cmpl %ecx, %edx
movl $.LC0, %esi
cmovg %edx, %ebx
cmovg %ecx, %ebp
... print code goes here ...
_Z6romanoiiii:
movq %rbx, -32(%rsp)
movq %rbp, -24(%rsp)
movl %edx, %ebx
movq %r12, -16(%rsp)
movq %r13, -8(%rsp)
movl %edi, %r12d
subq $40, %rsp
movl %esi, %r13d
cmpl %esi, %edi
movl %ecx, %ebp
cmovle %edi, %r13d
cmovle %esi, %r12d
movl $_ZSt4cout, %edi
cmpl %ecx, %edx
movl $.LC0, %esi
cmovle %edx, %ebp
cmovle %ecx, %ebx
... printout code here....
Come potete vedere, mine
e original
sono identici e romano
utilizza registri leggermente diversi e una diversa forma di cmov
, ma altrimenti fanno la stessa cosa con lo stesso numero di istruzioni.
Parentesi che risolve il problema ... –
Ancora un altro motivo per "non cercare di essere intelligenti" con espressioni condizionali e usare invece "if". Il compilatore farà la stessa cosa in ogni caso [presumendo che tu aggiunga la parentesi pertinente in modo che faccia ciò che realmente volevi]. Le espressioni ternarie possono essere utili a volte, ma questo è un buon esempio di cosa NON fare con loro. –
(E comunque, perché no: 'maxx = x1> x2? X1: x2'? –