Help with NaN handling in IL conditionals

L

Leon Lambert

I would appreciate it if someone could help me understand NaN handling
with respect to conditionals in IL code. I am playing with a small IL
interpreter and having a little problem with it. Following is a small
piece of C# code that gets compiled by VS2005 to simulate my problem.
static void Main(string[] args)
{
Double db1 = 1.0;
Double db2 = Double.NaN;
if (db1 <= db2)
db2 = 1.0;
}

This is very simple code. The assignment after the conditional should
never happen because the conditional should evaluate as false because of
the NaN. Following is the IL code generated.
..method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() =
( 01 00 00 00 )
// Code size 40 (0x28)
.maxstack 2
.locals init ([0] float64 db1,
[1] float64 db2,
[2] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.r8 1.
IL_000a: stloc.0
IL_000b: ldc.r8 (00 00 00 00 00 00 F8 FF)
IL_0014: stloc.1
IL_0015: ldloc.0
IL_0016: ldloc.1
IL_0017: cgt.un
IL_0019: stloc.2
IL_001a: ldloc.2
IL_001b: brtrue.s IL_0027
IL_001d: ldc.r8 1.
IL_0026: stloc.1
IL_0027: ret
} // end of method CABlock::Main
The IL code loads the two numbers onto the stack and does a cgt.un. This
should and does return a 0 (false) because of the NaN. The specification
is clear on this. The problem I am having is with the brtrue.s
instruction. The logic here seems to be reversed. If false is the result
of the cgt.un then the btrue will not branch which it should. This has
got me greatly confused. I have tried reading the CLR specification to
see what it does because it magically handles this properly. Somehow the
logic needs to get reversed but it is eluding me. The literal
interpretation of the above IL code only fails if there is a NaN
included. Without NaNs i have no problems at all.

Any help understanding the reversal of condition logic would be appreciated.

Leon Lambert
 
J

Jon Skeet [C# MVP]

Leon said:
I would appreciate it if someone could help me understand NaN handling
with respect to conditionals in IL code. I am playing with a small IL
interpreter and having a little problem with it. Following is a small
piece of C# code that gets compiled by VS2005 to simulate my problem.

The IL code loads the two numbers onto the stack and does a cgt.un. This
should and does return a 0 (false) because of the NaN. The specification
is clear on this. The problem I am having is with the brtrue.s
instruction. The logic here seems to be reversed.

Yes. It's doing:

if (db1 > db2)
{
// Branch!
}
else
{
db=1.0;
}

Note that cgt.un is a "greater than" even though your C# code specifies
"less than or equal to". Basically, it's reversing the logic to get the
"less than or equal to" behaviour because there's only "less than" or
"greater than" in IL (I believe!).

Apologies if you understood all this already and the problem is
slightly different...

Jon
 
W

Willy Denoyette [MVP]

Hmmmm... your assumption that cgt.un return 0 is wrong.
It's actually 1.

cgt.un returns 1 if value1 is greater than value2 or if value1 is
'unordered'.
Now, value2 is the value at the top of the stack, so it's the value 1.0.
value1 is Nan that means unordered.
This results in a value 1 returned, right?

Willy.
 
L

Leon Lambert

The following is in the spec about cgt.un.

"Description:
The cgt instruction compares value1 and value2. If value1 is strictly
greater than value2, then 1 (of type int32) is pushed on the stack.
Otherwise 0 (of type int32) is pushed on the stack

For floating-point numbers, cgt returns 0 if the numbers are unordered
(that is, if one or both of the arguments are NaN).

As per IEC 60559:1989 spec, infinite values are ordered with respect to
normal numbers (e.g +infinity > 5.0 > -infinity)."

Doesn't this mean it returns 0 if either number is a Nan?

Leon Lambert
Hmmmm... your assumption that cgt.un return 0 is wrong.
It's actually 1.

cgt.un returns 1 if value1 is greater than value2 or if value1 is
'unordered'.
Now, value2 is the value at the top of the stack, so it's the value 1.0.
value1 is Nan that means unordered.
This results in a value 1 returned, right?

Willy.



I would appreciate it if someone could help me understand NaN handling with
respect to conditionals in IL code. I am playing with a small IL
interpreter and having a little problem with it. Following is a small piece
of C# code that gets compiled by VS2005 to simulate my problem.
static void Main(string[] args)
{
Double db1 = 1.0;
Double db2 = Double.NaN;
if (db1 <= db2)
db2 = 1.0;
}

This is very simple code. The assignment after the conditional should
never happen because the conditional should evaluate as false because of
the NaN. Following is the IL code generated.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (
01 00 00 00 )
// Code size 40 (0x28)
.maxstack 2
.locals init ([0] float64 db1,
[1] float64 db2,
[2] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.r8 1.
IL_000a: stloc.0
IL_000b: ldc.r8 (00 00 00 00 00 00 F8 FF)
IL_0014: stloc.1
IL_0015: ldloc.0
IL_0016: ldloc.1
IL_0017: cgt.un
IL_0019: stloc.2
IL_001a: ldloc.2
IL_001b: brtrue.s IL_0027
IL_001d: ldc.r8 1.
IL_0026: stloc.1
IL_0027: ret
} // end of method CABlock::Main
The IL code loads the two numbers onto the stack and does a cgt.un. This
should and does return a 0 (false) because of the NaN. The specification
is clear on this. The problem I am having is with the brtrue.s
instruction. The logic here seems to be reversed. If false is the result
of the cgt.un then the btrue will not branch which it should. This has got
me greatly confused. I have tried reading the CLR specification to see
what it does because it magically handles this properly. Somehow the logic
needs to get reversed but it is eluding me. The literal interpretation of
the above IL code only fails if there is a NaN included. Without NaNs i
have no problems at all.

Any help understanding the reversal of condition logic would be
appreciated.

Leon Lambert
 
L

Leon Lambert

Never mind i am an idiot. Been looking at spec for cgt instead of
cgt.un. Sometimes it pays to get some sleep Lol

Leon Lambert

Leon said:
The following is in the spec about cgt.un.

"Description:
The cgt instruction compares value1 and value2. If value1 is strictly
greater than value2, then 1 (of type int32) is pushed on the stack.
Otherwise 0 (of type int32) is pushed on the stack

For floating-point numbers, cgt returns 0 if the numbers are unordered
(that is, if one or both of the arguments are NaN).

As per IEC 60559:1989 spec, infinite values are ordered with respect to
normal numbers (e.g +infinity > 5.0 > -infinity)."

Doesn't this mean it returns 0 if either number is a Nan?

Leon Lambert
Hmmmm... your assumption that cgt.un return 0 is wrong.
It's actually 1.

cgt.un returns 1 if value1 is greater than value2 or if value1 is
'unordered'.
Now, value2 is the value at the top of the stack, so it's the value
1.0. value1 is Nan that means unordered.
This results in a value 1 returned, right?

Willy.



I would appreciate it if someone could help me understand NaN
handling with respect to conditionals in IL code. I am playing with a
small IL interpreter and having a little problem with it. Following
is a small piece of C# code that gets compiled by VS2005 to simulate
my problem.
static void Main(string[] args)
{
Double db1 = 1.0;
Double db2 = Double.NaN;
if (db1 <= db2)
db2 = 1.0;
}

This is very simple code. The assignment after the conditional should
never happen because the conditional should evaluate as false because
of the NaN. Following is the IL code generated.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() =
( 01 00 00 00 )
// Code size 40 (0x28)
.maxstack 2
.locals init ([0] float64 db1,
[1] float64 db2,
[2] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.r8 1.
IL_000a: stloc.0
IL_000b: ldc.r8 (00 00 00 00 00 00 F8 FF)
IL_0014: stloc.1
IL_0015: ldloc.0
IL_0016: ldloc.1
IL_0017: cgt.un
IL_0019: stloc.2
IL_001a: ldloc.2
IL_001b: brtrue.s IL_0027
IL_001d: ldc.r8 1.
IL_0026: stloc.1
IL_0027: ret
} // end of method CABlock::Main
The IL code loads the two numbers onto the stack and does a cgt.un.
This should and does return a 0 (false) because of the NaN. The
specification is clear on this. The problem I am having is with the
brtrue.s instruction. The logic here seems to be reversed. If false
is the result of the cgt.un then the btrue will not branch which it
should. This has got me greatly confused. I have tried reading the
CLR specification to see what it does because it magically handles
this properly. Somehow the logic needs to get reversed but it is
eluding me. The literal interpretation of the above IL code only
fails if there is a NaN included. Without NaNs i have no problems at
all.

Any help understanding the reversal of condition logic would be
appreciated.

Leon Lambert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top