compare floating point with given digit of precision

A

Alessio

It is possible ?
Suppose I've 2 values e.g. -2.3456 and -2.3478, it is possibile to
compare as -2.34 and -2.34 ( 2 decimal places ) ?

Thanks in advance,

Alessio
 
B

bartc

Alessio said:
It is possible ?
Suppose I've 2 values e.g. -2.3456 and -2.3478, it is possibile to compare
as -2.34 and -2.34 ( 2 decimal places ) ?

If you convert these to strings using, say, sprintf, and specify 2 decimal
places, then you can test the resulting strings for equality using
strcmp()==0.

If you want to test as binary, you can try taking the difference and seeing
whether the magnitude of this is less than 0.005.
 
B

bartc

bartc said:
If you convert these to strings using, say, sprintf, and specify 2 decimal
places, then you can test the resulting strings for equality using
strcmp()==0.

If you want to test as binary, you can try taking the difference and
seeing
whether the magnitude of this is less than 0.005.

I don't think this will work unless one of the values is already -2.34. The
string method is slower but has fewer problems:

int cmpdouble(double a,double b,char* fmt) {
char astr[42],bstr[42];

sprintf(astr,fmt,a);
sprintf(bstr,fmt,b);
return strcmp(astr,bstr)==0;
}

printf("EQ=%d\n",cmpdouble(a,b,"%6.2f"));

There still corner cases: where both values are approximately -2.345 for
example, one might round to -2.34, the other to -2.35.
 
K

Keith Thompson

Pietro Cerutti said:
What about:

#include <stdio.h>
#include <math.h>

int main(void)
{
double a = -2.3456,
b = -2.3478;

if (fabs(a-b) < 0.01)
puts("almost equal");
else
puts("not almost equal");

return (0);
}

Sure, if that's your definition of "almost equal".

Keep in mind that none of -2.3456, -2.3478, and -2.34 can be
represented exactly in binary floating-point.

Quick summary: Floating-point is hard.
 
D

David Mathog

Alessio said:
It is possible ?
Suppose I've 2 values e.g. -2.3456 and -2.3478, it is possibile to
compare as -2.34 and -2.34 ( 2 decimal places ) ?

Short answer is yes, this can be done, but more you look into it, the
more complex it will be to handle all the special cases. At least if
you want to work strictly on the numerical values without first
converting / truncating to a text format with sprintf.

If all the numbers will always be in a very small size range, for
instance between 1 and 10, then you can use simple code like:

if(abs(x-y) < .005)

which will work ok for

2.3456 vs. 2.3478

But now feed it these:

23456 vs. 23478
2.3456e30 vs. 2.34578e30

oops, the code doesn't scale. So you try ratios instead and use 1%
difference as an acceptable difference:

if(min(abs(x),abs(y))/max(abs(x),abs(y)) > .99)

That scales well but doesn't have exactly the properties you requested.
(And it doesn't handle 0 vs. 0 very well!)

Alternatively, you could rescale all numbers back into an acceptable
range by reducing to just the mantissa (in range 0->0.999999) if the
exponents are the same. Ah but what about 0.99 vs 1.01, which will be
..99e0 vs .101e1?

Special cases, lots of special cases. Which makes me wonder not so much
how to do this, but how to do this with the least number of lines of code.

Regards,

David Mathog
 
T

Tim Prince

Eric said:
Once you settle on a sufficiently precise definition of "compare
with two decimal places," there's almost certainly a way to do what
you need.
I might have guessed that OP wanted something like
rint(-2.3456*100) == rint(-2.3478*100)
but, as you say, many interpretations were left open.
 
A

Alessio

Eric Sosman ha scritto:
Probably, but first you need a clearer specification of what you
want. A few questions to consider:

- Should -2.3999 and -2.4001 be considered equal (they agree when
rounded to three significant digits) or unequal (their first three
digits do not agree)?
equal.


- Should -2.3400 and -2.3499 be considered equal (they agree in
their first three digits) or unequal (they are more than 0.005 apart)?
equal.


- Should -2345.6 and -2345.9 be considered equal (they agree to
three significant digits) or unequal (they disagree before the
thousandth's place)?
equal.


Once you settle on a sufficiently precise definition of "compare
with two decimal places," there's almost certainly a way to do what
you need.

I must to compare two files that contains floating-points values.
This is for test if I rightly ported a function from pv-wave to c.
I simply open and read the two outputs and put them into their relative
structure.
Now I'll try to use sprintf method, that I think should be much faster (
but I'm not care of it).

Thank you again,
Alessio.
 
K

Keith Thompson

Alessio said:
Eric Sosman ha scritto:

I must to compare two files that contains floating-points values.
This is for test if I rightly ported a function from pv-wave to c.
I simply open and read the two outputs and put them into their
relative structure.
Now I'll try to use sprintf method, that I think should be much faster
( but I'm not care of it).

You seem to have a good idea of what you want to do, but you've
expressed that idea through individual examples. Examples are
a great way to supplement an explanation, but they are not by
themselves an explanation, and it's possible that there's more than
one comparison algorithm that's consistent with what you've told
us so far.

Can you describe, either in English or in mathematical notation, what
it means for two numbers to be sufficiently close for your purposes?

Note that, for anything other than actual equality, you're going
to have one of the following situations for some values:
x == y, y == z, x != z
or
x == y, y != z, but y and z are much closer to each other than
x and y are
 
W

William Hughes

     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
 
N

Nobody

I must to compare two files that contains floating-points values.
This is for test if I rightly ported a function from pv-wave to c.
I simply open and read the two outputs and put them into their relative
structure.

In which case, I suggest comparing to within a given tolerance, as
suggested by Pietro.
Now I'll try to use sprintf method, that I think should be much faster

The sprintf() method is likely to be the slowest solution. Comparing to a
given tolerance, or rounding each value to a multiple of 0.01 will be
faster.
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top