1) you are trying to do equals comparisons with floating point. Some floating point formats that will work, but IEEE formats will not work. You cannot do equals comparisons. You need to convert that float to an int then do an int compare. With integers (not limiting myself to 32 bit or anything here) there is only one way to represent each number so you can do equals comparisons.
2) remember the floating point math is base 2 and you are asking to do base 10 stuff. so there are going to be conversion issues, truncation. Also I again assume you are using IEEE which means you have three rounding modes (base 2) so you have to deal with that as well. You will want to do some sort of double_to_integer((double*1000.0)+0.5) and compare those. I would not be surprised if you find corner cases that dont work.
More interesting information on this problem. Note that use of unions in this manner is not supported by the C standard, but often just happens to work...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
double trunc(double d)
{
return (d>0) ? floor(d) : ceil(d) ;
}
int comparedigits(float a , float b)
{
if (trunc(1000.0 * a) == trunc(1000.0 * b))
{
return 1;
}
return 0;
}
union
{
unsigned int ul;
float f;
} fun;
union
{
unsigned int ul[2];
double d;
} dun;
int main ( void )
{
float g;
float h;
int t;
g = 2.346;
h = 2.34599;
t = comparedigits(g,h);
printf("%u\n",t);
printf("raw\n");
fun.f=g; printf("0x%08X\n",fun.ul);
fun.f=h; printf("0x%08X\n",fun.ul);
dun.d=g; printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=h; printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("trunc\n");
dun.d=trunc(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=trunc(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("trunc\n");
dun.d=trunc(1000.0F * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=trunc(1000.0F * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("floor\n");
dun.d=floor(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=floor(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("ceil\n");
dun.d=ceil(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=ceil(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("%u\n",(unsigned int)(g*1000.0));
printf("%u\n",(unsigned int)(h*1000.0));
if (trunc(1000.0F * g) == trunc(1000.0F * h))
{
printf("true\n");
}
else
{
printf("false\n");
}
return(0);
}
compile and run
gcc test.c -o test -lm
./test
1
raw
0x401624DD
0x401624B3
0x4002C49B_A0000000
0x4002C496_60000000
trunc
0x40A25200_00000000
0x40A25200_00000000
trunc
0x40A25400_00000000
0x40A25200_00000000
floor
0x40A25200_00000000
0x40A25200_00000000
ceil
0x40A25400_00000000
0x40A25400_00000000
2345
2345
false
So doing the 1000 * x in single math instead of double math appears to fix the problem
1000.0 * a is mixed mode. The 1000.0 is a double by the C standards unless specified to be a single. And a is a single, so a is converted to a double the math is done as double then it is fed to a double function. 1000.0F is a single, a is a single, so the multiply is done as single math, then it is converted to double. So perhaps the real issue is in the conversion and rounding of g and h to a double. Would have to dig more into the mantissa differences...
I think the key is this, the double times single 1000.0 * x results
trunc
0x40A25200_00000000
0x40A25200_00000000
If those are equal then anything you do the same number will come out the same. When it is a single times a single then converted to a double, they differ.
trunc
0x40A25400_00000000
0x40A25200_00000000
and that makes your code work (for those two specific values).
false