]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [section:float_comparison Floating-point Comparison] |
2 | ||
3 | [import ../../example/float_comparison_example.cpp] | |
4 | ||
5 | Comparison of floating-point values has always been a source of endless difficulty and confusion. | |
6 | ||
7 | Unlike integral values that are exact, all floating-point operations | |
8 | will potentially produce an inexact result that will be rounded to the nearest | |
9 | available binary representation. Even apparently inocuous operations such as assigning | |
10 | 0.1 to a double produces an inexact result (as this decimal number has no | |
11 | exact binary representation). | |
12 | ||
13 | Floating-point computations also involve rounding so that some 'computational noise' is added, | |
14 | and hence results are also not exact (although repeatable, at least under identical platforms and compile options). | |
15 | ||
16 | Sadly, this conflicts with the expectation of most users, as many articles and innumerable cries for help show all too well. | |
17 | ||
18 | Some background reading is: | |
19 | ||
20 | * Knuth D.E. The art of computer programming, vol II, section 4.2, especially Floating-Point Comparison 4.2.2, pages 198-220. | |
21 | * [@http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html David Goldberg, "What Every Computer Scientist Should Know About Floating-Point Arithmetic"] | |
22 | * [@http://adtmag.com/articles/2000/03/16/comparing-floatshow-to-determine-if-floating-quantities-are-close-enough-once-a-tolerance-has-been-r.aspx | |
23 | Alberto Squassabia, Comparing floats listing] | |
24 | * [@https://code.google.com/p/googletest/wiki/AdvancedGuide#Floating-Point_Comparison Google Floating-Point_Comparison guide] | |
25 | * [@boost:/libs/test/doc/html/boost_test/users_guide/testing_tools/testing_floating_points.html Boost.Test Floating-Point_Comparison] | |
26 | ||
27 | Boost provides a number of ways to compare floating-point values to see if they are tolerably close enough to each other, | |
28 | but first we must decide what kind of comparison we require: | |
29 | ||
30 | * Absolute difference/error: the absolute difference between two values ['a] and ['b] is simply `fabs(a-b)`. | |
31 | This is the only meaningful comparison to make if we know that the result may have cancellation error (see below). | |
32 | * The edit distance between the two values: i.e. how many (binary) floating-point values are between two values ['a] and ['b]? | |
33 | This is provided by the function __float_distance, but is probably only useful when you know that the distance should be very small. | |
34 | This function is somewhat difficult to compute, and doesn't scale to values that are very far apart. In other words, use with care. | |
35 | * The relative distance/error between two values. This is quick and easy to compute, and is generally the method of choice when | |
36 | checking that your results are "tolerably close" to one another. However, it is not as exact as the edit distance when dealing | |
37 | with small differences, and due to the way floating-point values are encoded can "wobble" by a factor of 2 compared to the "true" | |
38 | edit distance. This is the method documented below: if `float_distance` is a surgeon's scalpel, then `relative_difference` is more | |
39 | like a Swiss army knife: both have important but different use cases. | |
40 | ||
41 | ||
42 | [h5:fp_relative Relative Comparison of Floating-point Values] | |
43 | ||
44 | ||
45 | `#include <boost/math/special_functions/relative_difference.hpp>` | |
46 | ||
47 | template <class T, class U> | |
48 | ``__sf_result`` relative_difference(T a, U b); | |
49 | ||
50 | template <class T, class U> | |
51 | ``__sf_result`` epsilon_difference(T a, U b); | |
52 | ||
53 | The function `relative_difference` returns the relative distance/error ['E] between two values as defined by: | |
54 | ||
55 | [pre E = fabs((a - b) / min(a,b))] | |
56 | ||
57 | The function `epsilon_difference` is a convenience function that returns `relative_difference(a, b) / eps` where | |
58 | `eps` is the machine epsilon for the result type. | |
59 | ||
60 | The following special cases are handled as follows: | |
61 | ||
62 | * If either of ['a] or ['b] is a NaN, then returns the largest representable value for T: for example for type `double`, this | |
63 | is `std::numeric_limits<double>::max()` which is the same as `DBL_MAX` or `1.7976931348623157e+308`. | |
64 | * If ['a] and ['b] differ in sign then returns the largest representable value for T. | |
65 | * If both ['a] and ['b] are both infinities (of the same sign), then returns zero. | |
66 | * If just one of ['a] and ['b] is an infinity, then returns the largest representable value for T. | |
67 | * If both ['a] and ['b] are zero then returns zero. | |
68 | * If just one of ['a] or ['b] is a zero or a denormalized value, then it is treated as if it were the | |
69 | smallest (non-denormalized) value representable in T for the purposes of the above calculation. | |
70 | ||
71 | These rules were primarily designed to assist with our own test suite, they are designed to be robust enough | |
72 | that the function can in most cases be used blindly, including in cases where the expected result is actually | |
73 | too small to represent in type T and underflows to zero. | |
74 | ||
75 | [h5 Examples] | |
76 | ||
77 | [compare_floats_using] | |
78 | ||
79 | [compare_floats_example_1] | |
80 | [compare_floats_example_2] | |
81 | [compare_floats_example_3] | |
82 | [compare_floats_example_4] | |
83 | [compare_floats_example_5] | |
84 | [compare_floats_example_6] | |
85 | ||
86 | All the above examples are contained in [@../../example/float_comparison_example.cpp float_comparison_example.cpp]. | |
87 | ||
88 | [h5:small Handling Absolute Errors] | |
89 | ||
90 | Imagine we're testing the following function: | |
91 | ||
92 | double myspecial(double x) | |
93 | { | |
94 | return sin(x) - sin(4 * x); | |
95 | } | |
96 | ||
97 | This function has multiple roots, some of which are quite predicable in that both | |
98 | `sin(x)` and `sin(4x)` are zero together. Others occur because the values returned | |
99 | from those two functions precisely cancel out. At such points the relative difference | |
100 | between the true value of the function and the actual value returned may be ['arbitrarily | |
101 | large] due to [@http://en.wikipedia.org/wiki/Loss_of_significance cancellation error]. | |
102 | ||
103 | In such a case, testing the function above by requiring that the values returned by | |
104 | `relative_error` or `epsilon_error` are below some threshold is pointless: the best | |
105 | we can do is to verify that the ['absolute difference] between the true | |
106 | and calculated values is below some threshold. | |
107 | ||
108 | Of course, determining what that threshold should be is often tricky, | |
109 | but a good starting point would be machine epsilon multiplied by the largest | |
110 | of the values being summed. In the example above, the largest value returned | |
111 | by `sin(whatever)` is 1, so simply using machine epsilon as the target for | |
112 | maximum absolute difference might be a good start (though in practice we may need | |
113 | a slightly higher value - some trial and error will be necessary). | |
114 | ||
115 | [endsect] [/section:float_comparison Floating-point comparison] | |
116 | ||
117 | [/ | |
118 | Copyright 2015 John Maddock and Paul A. Bristow. | |
119 | Distributed under the Boost Software License, Version 1.0. | |
120 | (See accompanying file LICENSE_1_0.txt or copy at | |
121 | http://www.boost.org/LICENSE_1_0.txt). | |
122 | ] |