mirror of
https://github.com/boostorg/test.git
synced 2026-02-14 13:12:10 +00:00
191 lines
13 KiB
HTML
191 lines
13 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
|
<HTML>
|
|
<HEAD>
|
|
<TITLE>The Test Tools: floating-point comparison algorithms</TITLE>
|
|
<LINK rel="stylesheet" type="text/css" href="../../style/btl.css" media="screen">
|
|
<LINK rel="stylesheet" type="text/css" href="../../style/btl-print.css" media="print">
|
|
<META http-equiv="Content-Language" content="en-us">
|
|
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
</HEAD>
|
|
<BODY>
|
|
<DIV class="header"> <A href="../../index.html">Boost.Test</A> > <A href="../index.html">Components</A>
|
|
> <A href="index.html">The Test Tools</A> > <SPAN class="current_article">Floating point comparison
|
|
algorithms</SPAN> </DIV>
|
|
<DIV class="body"> <IMG src="../../btl1.gif" width="252" height="43" alt="Boost Test logo">
|
|
<H1 class="subtitle">Floating-point comparison algorithms</H1>
|
|
<P class="page-toc"> <A href="#Introduction">Introduction</A><BR>
|
|
<A href="#tolerance">How to choose a tolerance</A><BR>
|
|
<A href="#close_at_tolerance">The close_at_tolerance algorithm</A><BR>
|
|
<A href="#check_is_close">The check_is_close algorithm</A><BR>
|
|
<A href="#check_is_small">The check_is_small algorithm</A><BR>
|
|
<A href="#Implementation">Implementation</A><BR>
|
|
<A href="#Acknowledgements">Acknowledgements</A><BR>
|
|
<A href="#References">References</A> </P>
|
|
<H2><A name="Introduction">Introduction</A></H2>
|
|
<P class="first-line-indented">In most cases it is unreasonable to use an operator==(...) for a floating-point
|
|
values equality check The simple solution like abs(f1-f2) <= e does not work for very small or
|
|
very big values. This floating-point comparison algorithm is based on the more confident solution
|
|
presented by Knuth in [1]. For a given floating point values <EM>u</EM> and
|
|
<EM>v</EM> and
|
|
a tolerance <EM>e</EM>:</P>
|
|
<TABLE class="2fields">
|
|
<TR>
|
|
<TD>
|
|
<P> |<I> u </I>-<I> v </I>| <= <I>e * |u|</I> and |<I> u </I>-<I>
|
|
v </I>| <= <I>e * |v|<BR>
|
|
</I>defines a "very close with tolerance <I>e</I>" relationship between <I>u</I> and
|
|
<I>v</I> </P>
|
|
</TD>
|
|
<TD valign="top"> (<B><A name="F.1">1</A></B>)</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>
|
|
<P> |<I> u </I>-<I> v </I>| <= <I>e * |u|</I> or |<I>
|
|
u </I>-<I> v </I>| <= <I>e * |v|<BR>
|
|
</I>defines a "close enough with tolerance <I>e</I>" relationship between <I>u</I>
|
|
and <I>v</I> </P>
|
|
</TD>
|
|
<TD valign="top"> (<A name="F.2"><B>2</B></A>) </TD>
|
|
</TR>
|
|
</TABLE>
|
|
<P class="first-line-indented">Both relationships are commutative but are not transitive. The relationship
|
|
defined by inequations (<B>1</B>) is stronger that the relationship defined by inequations (<B>2</B>)
|
|
(i.e. (<B>1</B>) => (<B>2</B>) ). Because of the multiplication in the right side of inequations,
|
|
that could cause an unwanted underflow condition, the implementation is using modified version of
|
|
the inequations (<B>1</B>) and (<B>2</B>) where all underflow, overflow conditions could be guarded
|
|
safely:</P>
|
|
<TABLE class="2fields">
|
|
<TR>
|
|
<TD>
|
|
<P> |<I> u </I>-<I> v </I>| / <I> |u| </I><= <I>e</I> and |<I> u </I>-<I>
|
|
v </I>| / <I> |v| </I><= <I>e<BR>
|
|
</I>|<I> u </I>-<I> v </I>| / <I> |u| </I> <= <I>e</I> or |<I> u </I>-<I> v </I>|
|
|
/ <I> |v| </I> <= <I>e</I> </P>
|
|
</TD>
|
|
<TD> (<B>1`</B>)<BR>
|
|
(<B>2`</B>) </TD>
|
|
</TR>
|
|
</TABLE>
|
|
<H2><A name="tolerance">Tolerance selection considerations</A></H2>
|
|
<P class="first-line-indented">In case of absence of a domain specific requirements the value of tolerance
|
|
could be chosen as a sum of the predicted upper limits for "relative rounding errors" of
|
|
compared values. The "rounding" is the operation by which a real value 'x' is represented
|
|
in a floating-point format with 'p' binary digits (bits) as the floating-point value 'X'. The "relative
|
|
rounding error" is the difference between the real and the floating point values in relation
|
|
to real value: |x-X|/|x|. The discrepancy between real and floating point value may be caused by several
|
|
reasons:</P>
|
|
<UL>
|
|
<LI>Type promotion</LI>
|
|
<LI>Arithmetic operations</LI>
|
|
<LI>Conversion from a decimal presentation to a binary presentation</LI>
|
|
<LI>Non-arithmetic operation</LI>
|
|
</UL>
|
|
<P class="first-line-indented">The first two operations proved to have a relative rounding error that
|
|
does not exceed 1/2 * "machine epsilon value" for the appropriate floating point type (represented
|
|
by std::numeric_limits<FPT>::epsilon()). Conversion to binary presentation, sadly, does not
|
|
have such requirement. So we can't assume that float 1.1 is close to real 1.1 with tolerance 1/2 *
|
|
"machine epsilon value" for float (though for 11./10 we can). Non arithmetic operations
|
|
either do not have a predicted upper limit relative rounding errors. Note that both arithmetic and
|
|
non arithmetic operations might also produce others "non-rounding" errors, such as underflow/overflow,
|
|
division-by-zero or 'operation errors'.</P>
|
|
<P class="first-line-indented">All theorems about the upper limit of a rounding error, including that
|
|
of 1/2*epsilon, refers <SPAN style="text-decoration: underline">only</SPAN> to the 'rounding' operation,
|
|
nothing more. This means that the 'operation error', that is, the error incurred by the operation
|
|
itself, besides rounding, isn't considered. In order for numerical software to be able to actually
|
|
predict error bounds, the IEEE754 standard requires arithmetic operations to be 'correctly or exactly
|
|
rounded'. That is, it is required that the internal computation of a given operation be such that
|
|
the floating point result is the <SPAN style="text-decoration: underline">exact</SPAN> result rounded
|
|
to the number of working bits. In other words, it is required that the computation used by the operation
|
|
itself doesn't introduce any additional errors. The IEEE754 standard does not require same behavior
|
|
from most non-arithmetic operation. The underflow/overflow and division-by-zero errors may cause rounding
|
|
errors with unpredictable upper limits.</P>
|
|
<P class="first-line-indented">At last be aware that 1/2*epsilon rules are not transitive. In other words
|
|
combination of two arithmetic operations may produce rounding error that significantly exceed 2*1/2*epsilon.
|
|
All in all there is no generic rules on how to select the tolerance and users need to apply common sense and
|
|
domain/problem specific knowledge to decide on tolerance value. </P>
|
|
<P class="first-line-indented">To simplify things in most usage cases latest version of algorithm below opted
|
|
to use percentage values for tolerance specification (instead of fractions of related values). In other words
|
|
now you use it to check that difference between two values does not exceed x percent. </P>
|
|
<P class="first-line-indented">For more reading about floating-point comparison see references at the
|
|
end.</P>
|
|
<H2>The <A name="close_at_tolerance">close_at_tolerance</A> algorithm</H2>
|
|
<P>The <SPAN class="new-term"> close_at_tolerance</SPAN> algorithm allows to check the relationship
|
|
defines by inequations (<B><A href="#F.1">1</A></B>) or (<B><A href="#F.2">2</A></B>). It is implemented
|
|
as binary predicate.</P>
|
|
<PRE class="code"><SPAN class="reserv-word">enum</SPAN> floating_point_comparison_type { FPC_STRONG, FPC_WEAK };
|
|
|
|
<SPAN class="reserv-word">template</SPAN><<SPAN class="reserv-word">typename</SPAN> FPT, <SPAN class="reserv-word">typename</SPAN> PersentType = FPT>
|
|
<SPAN class="reserv-word">class</SPAN> <SPAN class="new-term">close_at_tolerance</SPAN>
|
|
{
|
|
<SPAN class="reserv-word">public</SPAN>:
|
|
<SPAN class="reserv-word">explicit</SPAN> close_at_tolerance( PersentType percentage_tolerance, floating_point_comparison_type fpc_type = FPC_STRONG );
|
|
|
|
<SPAN class="cpp-type">bool</SPAN> <SPAN class="reserv-word">operator</SPAN>()( FPT left, FPT right ) <SPAN class="reserv-word">const</SPAN>;
|
|
};</PRE>
|
|
<P class="first-line-indented">The first constructor allows to specify a percentage
|
|
tolerance value to compare against. The fpc_type switch allows to select
|
|
comparison type. The default behavior is to check strong relationship defined
|
|
by inequations (<B><A href="#F.1">1</A></B>). Use FPC_WEAK to check weak
|
|
relationship defined by inequations (<B><A href="#F.2">2</A></B>)</P>
|
|
<H2>The <A name="check_is_close">check_is_close</A> algorithm</H2>
|
|
<P>The <SPAN class="new-term">check_is_close</SPAN> algorithm present an alternative interface for the close_at_tolerance algorithm.<SPAN class="new-term">check_is_close</SPAN> is defined as predicate with four arguments </P>
|
|
<PRE class="code"><SPAN class="reserv-word">struct</SPAN> check_is_close_t {
|
|
<SPAN class="reserv-word">typedef</SPAN> <SPAN class="cpp-type">bool</SPAN> result_type;
|
|
|
|
template<<SPAN class="reserv-word">typename</SPAN> FPT, <SPAN class="reserv-word">typename</SPAN> PersentType>
|
|
<SPAN class="cpp-type">bool</SPAN>
|
|
operator()( FPT left, FPT right, PersentType percentage_tolerance, floating_point_comparison_type fpc_type = FPC_STRONG );
|
|
};
|
|
|
|
<SPAN class="reserv-word">namespace</SPAN> {
|
|
check_is_close_t check_is_close;
|
|
}</PRE>
|
|
<H2>The <A name="check_is_small">check_is_small</A> algorithm</H2>
|
|
<P>The <SPAN class="new-term">check_is_small</SPAN> algorithm checks that absolute value of the argument is small enough. Absolute value of the tolerance is supplied as a second argument. check_is_small is defined as binary predicate.</P>
|
|
<PRE class="code"><SPAN class="reserv-word">struct</SPAN> check_is_close_t {
|
|
<SPAN class="reserv-word">typedef</SPAN> <SPAN class="cpp-type">bool</SPAN> result_type;
|
|
|
|
template<<SPAN class="reserv-word">typename</SPAN> FPT>
|
|
<SPAN class="cpp-type">bool</SPAN>
|
|
operator()( FPT fpv, FPT tolerance );
|
|
};
|
|
|
|
<SPAN class="reserv-word">namespace</SPAN> {
|
|
check_is_small_t check_is_small;
|
|
}</PRE>
|
|
<SPAN class="first-line-indented">In spite of the fact that it's not recommended to use absolute values for floating point values comparisons the need for simple "is it small" checks is arousing from time to time. This is an algorithms to fill this niche.</SPAN>
|
|
<H2><A name="Implementation">Implementation</A></H2>
|
|
<P class="first-line-indented">The all algorithms are implemented in the header file <A href="../../../../../boost/test/floating_point_comparison.hpp">
|
|
floating_point_comparison.hpp</A>. It is recommended to use test tools wrappers located on <A href="../../../../../boost/test/test_tools.hpp">test_tools.hpp</A>.
|
|
Note that you still need to include <A href="../../../../../boost/test/floating_point_comparison.hpp"> floating_point_comparison.hpp</A>
|
|
yourself since it does not get included automatically.</P>
|
|
<H2><A name="Acknowledgements">Acknowledgement</A>s</H2>
|
|
<P class="first-line-indented">Fernando Cacciola for very helpful discussion of floating point arithmetic
|
|
on the boost forum.</P>
|
|
<H2><A name="References">References</A></H2>
|
|
<P>[1] Knuth D.E. <I>The art of computer programming</I> (vol II).<BR>
|
|
[2] David Goldberg <A href="http://citeseer.ist.psu.edu/goldberg91what.html"> What Every Computer Scientist
|
|
Should Know About Floating-Point Arithmetic</A> <BR>
|
|
[3] Kulisch U. <A href="http://www.eyrolles.com/php.sciences/Ouvrages/9783211838709.php3?xd=ed1884aaa411937c861a27010830ff23">
|
|
Rounding near zero</A>.<BR>
|
|
[4] Philippe Langlois <A href="http://www.inria.fr/rrrt/rr-3967.html">From Rounding Error Estimation
|
|
to Automatic Correction with Automatic Differentiation</A><BR>
|
|
[5] Lots of information on William Kahan <A href="http://www.cs.berkeley.edu/~wkahan/">home page</A><BR>
|
|
[4] Alberto Squassabia <A href="http://www.adtmag.com/joop/crarticle.asp?ID=396">Comparing Floats:
|
|
How To Determine if Floating Quantities Are Close Enough Once a Tolerance Has Been Reached</A> C++
|
|
Report March 2000.<BR>
|
|
[5] Pete Becker The Journeyman's Shop: Trap Handlers, Sticky Bits, and Floating-Point Comparisons
|
|
C/C++ Users Journal December 2000. </P>
|
|
</DIV>
|
|
<DIV class="footer">
|
|
<DIV class="footer-body">
|
|
<P> © <A name="Copyright">Copyright</A> <A href="mailto:boost-test%20at%20emailaccount%20dot%20com%20%28please%20unobscure%29">Gennadiy Rozental</A> 2001-2006. <BR>
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file <A href="../../../../../LICENSE_1_0.txt">LICENSE_1_0.txt</A> or copy at
|
|
<A href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</A>)</P>
|
|
<P>Revised: <!-- #BeginDate format:Sw1 -->28 February, 2006<!-- #EndDate --> </P>
|
|
</DIV>
|
|
</DIV>
|
|
</BODY>
|
|
</HTML>
|