From your responses, it seems you want to check whether two
F-P values agree to plus-or-minus a "relative error," that is, a
stated percent or fraction of their value. (Contrast this with
"absolute error," where you test for agreement to plus-or-minus
a fixed amount.) Given x and y, you want to know whether they
agree to plus-or-minus 0.1% (or whatever).
There's one philosophical question you need to settle first,
though: Do x and y have equal status, or is x "authoritative"
and y "in question?" That is, do you want to test whether y
is "close to" x, or do you want to test whether x and y are
"close?" These are not quite the same test, even though they
seem similar at first glance.
Let's take the first one first: x is the "authoritative"
value, and you want to know whether y is within 0.1% of x.
The base for the percentage is clearly the value of x, so the
test is pretty simple: `if (x*0.999 <= y && y < x*1.001)' tests
whether y is within the 0.1% x-centered range. Alternatively,
you could write `if (fabs(x-y) < 0.001*fabs(x))'. (Obviously,
the numbers like 0.001 would change if you wanted a coarser
or finer tolerance; I've just picked this value for illustration.)
If x and y are "equally suspect," neither having any particular
claim to being "right," the 0.1% figure is a little harder to apply
because it's not clear whether the base for the 0.1% should be x,
or y, or something else. One common choice for the base is to
use whichever of x or y has the larger magnitude:
double xabs = fabs(x);
double yabs = fabs(y);
double base = xabs > yabs ? xabs : yabs;
if (fabs(x-y) < 0.001*base) ...
Another possibility is to use the value halfway between them:
if (fabs(x-y) < 0.001*fabs((x+y)*0.5)) ...
So the pv-wave values might be the "authoritative reference" and
the others the "to be tested suspects." (On the other hand, you might
treat the two value sets as "equally unreliable," on the grounds that
pv-wave is not proven to be 100% error-free. Your choice.)
It'll probably be slow, since converting a floating-point number
to a decimal string is a fairly involved process. Also, it will
restrict the possible tolerances you could use: You could only test
for agreement to so-and-so many decimal places, and you couldn't
check for a tolerance of something like 0.3%. For reasons of speed
and flexibility, I'd suggest working directly with the numbers as
above, rather than with their decimal representations.
(Also, the string comparison must be done carefully. Note that
the first three characters of "123.456" agree with the first three
characters of "12399999999999999999" ...)
(Note that most of this has little to do
with C. The problems of floating point occur
in any language)
Testing if floating point output from A
agrees with floating point output from B
is non-trivial. Because of library and
type size issues, the best you can hope
for is "close". At test with tolerance
0.5e-n is very similar to a test to
n significant digits.
You have an issue if either X or Y is 0.
Here you can only use absolute error. What
you need to know is if both X and Y are
essentially zero. But what "essentially zero"
means varies with both the problem and the
data type used (rule of thumb, if the expected values
are about 1 (e.g. between 100 and 1/100) then
use 10e-5 for float and 10e-12 for double).
Untested code
int check_float_equal(float x, float y)
{
const double tol=0.5e-3;
const double essentially_zero = 10e-12;
/* Note that this will compare x and y equal
* if x is close to but not essentially zero
* and y is essentially zero but close to not
* essentially zero. If you do not want this
* behaviour add a test to the else
*/
equal = FALSE;
if (fabs(x) < essentially zero)
{
if (fabs(y) < essentially_zero)
{
equal = TRUE;
}
}
else
{
if( fabs(x-y) < tol*fabs((x+y)*0.5) )
{
equal = TRUE;
}
}
return equal;
}
- William Hughes