Line data Source code
1 : // Snap Websites Server -- handle the basic display of the website content
2 : // Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "snapwebsites/floats.h"
22 :
23 :
24 : // C++ lib
25 : //
26 : #include <cstdint>
27 :
28 :
29 : // last include
30 : //
31 : #include <snapdev/poison.h>
32 :
33 :
34 :
35 : namespace snap
36 : {
37 :
38 : /** \brief Compare two floating points together.
39 : *
40 : * Comparing two floating point numbers requires some work if you want to
41 : * make sure that you do get true when the floats are (nearly) equal, then
42 : * you want to compare the mantissa and not the floats as is.
43 : *
44 : * The function compares the signs, unless a and b are +0.0 and -0.0 if the
45 : * signs are different the floating points are considered different.
46 : *
47 : * Next the function extracts the mantissa and shifts it before comparing
48 : * the absolute value of the difference against the value defined in
49 : * epsilon.
50 : *
51 : * \note
52 : * At this time the compare ignores whether one of the input is a NaN.
53 : *
54 : * \param[in] a The left hand side floating point value.
55 : * \param[in] b The right hand side floating point value.
56 : * \param[in] epsilon The maximum delta between a and b.
57 : *
58 : * \return true if a and b are considered equal.
59 : */
60 : template<typename T, typename I, int mantissa, int exponent, I epsilon>
61 0 : bool almost_equal(T a, T b)
62 : {
63 : #pragma GCC diagnostic push
64 : #pragma GCC diagnostic ignored "-Wfloat-equal"
65 : // quick test as is first (works great for 0.0 == 0.0)
66 : // (so the warning is okay, we're under control)
67 0 : if(a == b)
68 : #pragma GCC diagnostic pop
69 : {
70 0 : return true;
71 : }
72 :
73 : // different signs means they do not match.
74 0 : bool const sa(a < static_cast<T>(0.0));
75 0 : bool const sb(b < static_cast<T>(0.0));
76 0 : if(sa ^ sb)
77 : {
78 : // we already checked for +0.0 == -0.0
79 0 : return false;
80 : }
81 :
82 : union float_t
83 : {
84 : T f_float;
85 : I f_integer;
86 : };
87 : float_t fa, fb;
88 0 : fa.f_float = a;
89 0 : fb.f_float = b;
90 :
91 : // retrieve the mantissa
92 0 : I ia(fa.f_integer & (static_cast<I>(-1) >> (sizeof(I) * 8 - mantissa)));
93 0 : I ib(fb.f_integer & (static_cast<I>(-1) >> (sizeof(I) * 8 - mantissa)));
94 :
95 : // retrieve the exponent
96 0 : I ea((fa.f_integer >> mantissa) & (static_cast<I>(-1) >> (sizeof(I) * 8 - exponent)));
97 0 : I eb((fb.f_integer >> mantissa) & (static_cast<I>(-1) >> (sizeof(I) * 8 - exponent)));
98 :
99 : // adjust the mantissa with the exponent
100 : // TBD: ameliorate to try to keep as many bits as possible?
101 0 : if(ea < eb)
102 : {
103 0 : I d(eb - ea);
104 0 : ib >>= d;
105 : }
106 0 : else if(ea > eb)
107 : {
108 0 : I d(eb - ea);
109 0 : ia >>= d;
110 : }
111 :
112 : // Compare the mantissa
113 0 : I p(ia > ib ? ia - ib : ib - ia);
114 0 : return p < epsilon;
115 : }
116 :
117 :
118 : /** \brief Compare two float numbers against each others.
119 : *
120 : * This function compares two float numbers against each others and return
121 : * true if they are considered equal.
122 : *
123 : * \param[in] a The left hand side floating point value.
124 : * \param[in] b The right hand side floating point value.
125 : *
126 : * \return true if the two floats are close.
127 : */
128 0 : bool compare_floats(float a, float b)
129 : {
130 0 : return almost_equal<float, int32_t, 23, 8, 0x20>(a, b);
131 : }
132 :
133 :
134 : /** \brief Compare two double numbers against each others.
135 : *
136 : * This function compares two double numbers against each others and return
137 : * true if they are considered equal.
138 : *
139 : * \param[in] a The left hand side floating point value.
140 : * \param[in] b The right hand side floating point value.
141 : *
142 : * \return true if the two floats are close.
143 : */
144 0 : bool compare_floats(double a, double b)
145 : {
146 0 : return almost_equal<double, int64_t, 52, 11, 0x80>(a, b);
147 : }
148 :
149 :
150 : /** \fn compare_floats(double a, float b)
151 : * \brief Compare two floating point numbers against each others.
152 : *
153 : * This function compares a float with a double number after converting
154 : * the float to a double. The function returns true if they are
155 : * considered equal.
156 : *
157 : * \param[in] a The left hand side floating point value.
158 : * \param[in] b The right hand side floating point value.
159 : *
160 : * \return true if the two floats are close.
161 : */
162 0 : bool compare_floats(double a, float b)
163 : {
164 0 : return compare_floats(a, static_cast<double>(b));
165 : }
166 :
167 :
168 : /** \fn compare_floats(float a, double b)
169 : * \brief Compare two floating point numbers against each others.
170 : *
171 : * This function compares a float with a double number after converting
172 : * the float to a double. The function returns true if they are
173 : * considered equal.
174 : *
175 : * \param[in] a The left hand side floating point value.
176 : * \param[in] b The right hand side floating point value.
177 : *
178 : * \return true if the two floats are close.
179 : */
180 0 : bool compare_floats(float a, double b)
181 : {
182 0 : return compare_floats(static_cast<double>(a), b);
183 : }
184 :
185 :
186 : } // namespace snap
187 : // vim: ts=4 sw=4 et
|