There's no difference in any real-world sense.
Let's take a look at some code generated by various compilers for various targets.
- I'm assuming a signed int operation (which seem the intent of the OP)
- I've limited by survey to C and to compilers that I have readily at hand (admittedly a pretty small sample - GCC, MSVC and IAR)
- basic optimizations enabled (
-O2
for GCC, /Ox
for MSVC, -Oh
for IAR)
using the following module:
void my_puts(char const* s);
void cmp_gt(int x)
{
if (x > -1) {
my_puts("non-negative");
}
else {
my_puts("negative");
}
}
void cmp_gte(int x)
{
if (x >= 0) {
my_puts("non-negative");
}
else {
my_puts("negative");
}
}
And here's what each of them produced for the comparison operations:
MSVC 11 targeting ARM:
// if (x > -1) {...
00000 |cmp_gt| PROC
00000 f1b0 3fff cmp r0,#0xFFFFFFFF
00004 dd05 ble |$LN2@cmp_gt|
// if (x >= 0) {...
00024 |cmp_gte| PROC
00024 2800 cmp r0,#0
00026 db05 blt |$LN2@cmp_gte|
MSVC 11 targeting x64:
// if (x > -1) {...
cmp_gt PROC
00000 83 f9 ff cmp ecx, -1
00003 48 8d 0d 00 00 // speculative load of argument to my_puts()
00 00 lea rcx, OFFSET FLAT:$SG1359
0000a 7f 07 jg SHORT $LN5@cmp_gt
// if (x >= 0) {...
cmp_gte PROC
00000 85 c9 test ecx, ecx
00002 48 8d 0d 00 00 // speculative load of argument to my_puts()
00 00 lea rcx, OFFSET FLAT:$SG1367
00009 79 07 jns SHORT $LN5@cmp_gte
MSVC 11 targeting x86:
// if (x > -1) {...
_cmp_gt PROC
00000 83 7c 24 04 ff cmp DWORD PTR _x$[esp-4], -1
00005 7e 0d jle SHORT $LN2@cmp_gt
// if (x >= 0) {...
_cmp_gte PROC
00000 83 7c 24 04 00 cmp DWORD PTR _x$[esp-4], 0
00005 7c 0d jl SHORT $LN2@cmp_gte
GCC 4.6.1 targeting x64
// if (x > -1) {...
cmp_gt:
.seh_endprologue
test ecx, ecx
js .L2
// if (x >= 0) {...
cmp_gte:
.seh_endprologue
test ecx, ecx
js .L5
GCC 4.6.1 targeting x86:
// if (x > -1) {...
_cmp_gt:
mov eax, DWORD PTR [esp+4]
test eax, eax
js L2
// if (x >= 0) {...
_cmp_gte:
mov edx, DWORD PTR [esp+4]
test edx, edx
js L5
GCC 4.4.1 targeting ARM:
// if (x > -1) {...
cmp_gt:
.fnstart
.LFB0:
cmp r0, #0
blt .L8
// if (x >= 0) {...
cmp_gte:
.fnstart
.LFB1:
cmp r0, #0
blt .L2
IAR 5.20 targeting an ARM Cortex-M3:
// if (x > -1) {...
cmp_gt:
80B5 PUSH {R7,LR}
.... LDR.N R1,??DataTable1 ;; `?<Constant "non-negative">`
0028 CMP R0,#+0
01D4 BMI.N ??cmp_gt_0
// if (x >= 0) {...
cmp_gte:
80B5 PUSH {R7,LR}
.... LDR.N R1,??DataTable1 ;; `?<Constant "non-negative">`
0028 CMP R0,#+0
01D4 BMI.N ??cmp_gte_0
If you're still with me, here are the differences of any note between evaluating (x > -1)
and (x >= 0)
that show up:
- MSVC targeting ARM uses
cmp r0,#0xFFFFFFFF
for (x > -1)
vs cmp r0,#0
for (x >= 0)
. The first instruction's opcode is two bytes longer. I suppose that may introduce some additional time, so we'll call this an advantage for (x >= 0)
- MSVC targeting x86 uses
cmp ecx, -1
for (x > -1)
vs test ecx, ecx
for (x >= 0)
. The first instruction's opcode is one byte longer. I suppose that may introduce some additional time, so we'll call this an advantage for (x >= 0)
Note that GCC and IAR generated identical machine code for the two kinds of comparison (with the possible exception of which register was used). So according to this survey, it appears that (x >= 0)
has an ever-so-slight chance of being 'faster'. But whatever advantage the minimally shorter opcode byte encoding might have (and I stress might have) will be certainly completely overshadowed by other factors.
I'd be surprised if you found anything different for the jitted output of Java or C#. I doubt you'd find any difference of note even for a very small target like an 8 bit AVR.
In short, don't worry about this micro-optimization. I think my write up here has already spent more time than will be spent by any difference in the performance of these expressions accumulated across all the CPUs executing them in my lifetime. If you have the capability to measure the difference in performance, please apply your efforts to something more important like studying the behavior of sub-atomic particles or something.
x
is...? – Piscina>
and>=
are overloaded. – Bresciax
is auint
type... – Piscinax
was anunsigned short
and anint
could represent all possible values of anunsigned short
thenx > -1
would be always true. – Schistx >= 1
is the same condition asx != 0
, and is a special case in asm. (ISAs with condition flags normally have a Zero flag that indicates whether the last ALU operation's result was zero or not. So you could often avoid actually doing a compare or test ifx
was the result of an ALU operation. Of course, that's already true forx >= 0
andx > -1
signed conditions, where you can just check SF, the sign flag, on 2's complement machines that update a flag based on the MSB of the result.) – Loxodromic