[rrd-developers] Wrong behaviour or not?

Alex van den Bogaerdt alex at ergens.op.het.net
Fri Mar 7 05:38:05 CET 2008

Hi,

This seems wrong to me:

CDEF:y=x,b,c,IF

When x is anything but zero, b is returned. Anything includes NaN here.

The problem seems to be in the way C handles NaN comparisons in
combination with this code:

if (x != 0.0) ...

which does indeed branch to TRUE.
(similar code is in rrd_rpncalc.c, case OP_IF: )

I did a quick test:
(crappy C code attached)

Using != 0:

x==nan -> True
x==inf -> True
x==-inf -> True
x==1.000000 -> True
x==0.500000 -> True
x==0.000000 -> False
x==-0.500000 -> True

Comparing to greater than zero also doesn't work out nicely,
as it doesn't handle negative numbers. Otherwise it is much
better (IMHO!) than the current situation.

Using > 0:

x==nan -> False
x==inf -> True
x==-inf -> False
x==1.000000 -> True
x==0.500000 -> True
x==0.000000 -> False
x==-0.500000 -> False

If we only look at NaN, True or False (UNKN,0,1), we get:

Using != 0:
x==nan -> True
x==1.000000 -> True
x==0.000000 -> False

Using any of the three alternatives in my code:
x==nan -> False
x==1.000000 -> True
x==0.000000 -> False

I believe the latter to be more correct. As mentioned, this does
not handle "negative true".

There's an even better alternative. Replace "x!=0.0?true:false"
with "finite(x)?x?true:false:false"

Using finite and boolean:
x==nan -> False
x==inf -> False
x==-inf -> False
x==1.000000 -> True
x==0.500000 -> True
x==0.000000 -> False
x==-0.500000 -> True

A variant which considers infinite to be boolean true:
"isnan(x)?false:x?true:false"

Using isnan and boolean:
x==nan -> False
x==inf -> True
x==-inf -> True
x==1.000000 -> True
x==0.500000 -> True
x==0.000000 -> False
x==-0.500000 -> True

Another obvious alternative is to propagate NaN, perhaps something
like this (untested!):

case OP_IF:
if (! isnan(rpnstack -> s[stptr-2]))
rpnstack->s[stptr-2] = rpnstack->s[stptr-2] != 0.0 ?
rpnstack->s[stptr-1] : rpnstack->s[stptr];
stptr-=2;
break;

-------------- next part --------------
#include <stdio.h>
#include <math.h>
#define NAN_FUNC (double)(0.0/0.0)
#define INF_FUNC (double)(1.0/0.0)
#define NEGINF_FUNC (double)(-1.0/0.0)

void do_test1(double input) {
printf("x==%lf -> %s\n",input,input!=0.0?"True":"False");
}
void do_test2(double input) {
printf("x==%lf -> %s\n",input,input>0.0?"True":"False");
}
void do_test3(double input) {
printf("x==%lf -> %s\n",input,finite(input)?input?"True":"False":"False");
}
void do_test4(double input) {
printf("x==%lf -> %s\n",input,isnan(input)?"False":input?"True":"False");
}
int main(void) {
double x;

printf("Using != 0:\n");
do_test1(NAN_FUNC);
do_test1(INF_FUNC);
do_test1(NEGINF_FUNC);
do_test1(2.0/2.0);
do_test1(1.0/2.0);
do_test1(0.0/2.0);
do_test1(-1.0/2.0);
printf("\nUsing > 0:\n");
do_test2(NAN_FUNC);
do_test2(INF_FUNC);
do_test2(NEGINF_FUNC);
do_test2(2.0/2.0);
do_test2(1.0/2.0);
do_test2(0.0/2.0);
do_test2(-1.0/2.0);
printf("\nUsing finite and boolean:\n");
do_test3(NAN_FUNC);
do_test3(INF_FUNC);
do_test3(NEGINF_FUNC);
do_test3(2.0/2.0);
do_test3(1.0/2.0);
do_test3(0.0/2.0);
do_test3(-1.0/2.0);
printf("\nUsing isnan and boolean:\n");
do_test4(NAN_FUNC);
do_test4(INF_FUNC);
do_test4(NEGINF_FUNC);
do_test4(2.0/2.0);
do_test4(1.0/2.0);
do_test4(0.0/2.0);
do_test4(-1.0/2.0);

return 0;
}