Hello,
please advise on the following issue. I need to check that difference
between two double values is not higher than defined limit.
int main()
{
double limit = 0.3;
double val1 = 0.5, val2 = 0.2;
if ( (val1 - val2) > limit )
puts("Difference higher than limit!");
return 0;
}
This program displays message due to undefined binary presentation of
double, so result is unexpected. I see that it is behavior by
standard. But what can I do to make this code work as expected, i.e.
treat 0.3 as 0.3, not as (for example) 0.3000000000000000012? Are
there any standard ways (macros, functions) to fix the precision for
double integers? Everything I found is just formatting for input-
output functions, but it doesn't affect the binary presentation, just
visualisation.
The type of double is not integral unless you store integers in it.
It is floating point (which is really a base 2 fractional
representation internally).
Typically, numbers like 0.3 and 1.7 cannot be stored as exact values
in floating point representation.
Here is a very good document that explains floating point in html
format:
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Here is the same thing as a PDF file:
http://cch.loria.fr/documentation/IEEE754/ACM/goldberg.pdf
Related information from the C-FAQ:
14.1: When I set a float variable to, say, 3.1, why is printf printing
it as 3.0999999?
A: Most computers use base 2 for floating-point numbers as well as
for integers. In base 2, one divided by ten is an infinitely-
repeating fraction (0.0001100110011...), so fractions such as
3.1 (which look like they can be exactly represented in decimal)
cannot be represented exactly in binary. Depending on how
carefully your compiler's binary/decimal conversion routines
(such as those used by printf) have been written, you may see
discrepancies when numbers (especially low-precision floats) not
exactly representable in base 2 are assigned or read in and then
printed (i.e. converted from base 10 to base 2 and back again).
See also question 14.6.
4.4: My floating-point calculations are acting strangely and giving
me different answers on different machines.
A: First, see question 14.2 above.
If the problem isn't that simple, recall that digital computers
usually use floating-point formats which provide a close but by
no means exact simulation of real number arithmetic. Underflow,
cumulative precision loss, and other anomalies are often
troublesome.
Don't assume that floating-point results will be exact, and
especially don't assume that floating-point values can be
compared for equality. (Don't throw haphazard "fuzz factors"
in, either; see question 14.5.)
These problems are no worse for C than they are for any other
computer language. Certain aspects of floating-point are
usually defined as "however the processor does them" (see also
question 11.34), otherwise a compiler for a machine without the
"right" model would have to do prohibitively expensive
emulations.
This article cannot begin to list the pitfalls associated with,
and workarounds appropriate for, floating-point work. A good
numerical programming text should cover the basics; see also the
references below.
References: Kernighan and Plauger, _The Elements of Programming
Style_ Sec. 6 pp. 115-8; Knuth, Volume 2 chapter 4; David
Goldberg, "What Every Computer Scientist Should Know about
Floating-Point Arithmetic".
14.5: What's a good way to check for "close enough" floating-point
equality?
A: Since the absolute accuracy of floating point values varies, by
definition, with their magnitude, the best way of comparing two
floating point values is to use an accuracy threshold which is
relative to the magnitude of the numbers being compared. Rather
than
double a, b;
...
if(a == b) /* WRONG */
use something like
#include <math.h>
if(fabs(a - b) <= epsilon * fabs(a))
for some suitably-chosen degree of closeness epsilon (as long as
a is nonzero!).
References: Knuth Sec. 4.2.2 pp. 217-8.
14.6: How do I round numbers?
A: The simplest and most straightforward way is with code like
(int)(x + 0.5)
This technique won't work properly for negative numbers,
though (for which you could use something like
(int)(x < 0 ? x - 0.5 : x + 0.5)).