MySQL  8.0.27
Source Code Documentation
float_compare.h
Go to the documentation of this file.
1 #ifndef FLOAT_COMPARE_INCLUDED
2 #define FLOAT_COMPARE_INCLUDED
3 /* Copyright (c) 2016, 2021, Oracle and/or its affiliates.
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License, version 2.0,
7  as published by the Free Software Foundation.
8 
9  This program is also distributed with certain software (including
10  but not limited to OpenSSL) that is licensed under separate terms,
11  as designated in a particular file or component or in included license
12  documentation. The authors of MySQL hereby grant you an additional
13  permission to link the program and your derivative works with the
14  separately licensed software that they have included with MySQL.
15 
16  This program is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  GNU General Public License, version 2.0, for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with this program; if not, write to the Free Software
23  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
24 
25 /**
26  @file sql/float_compare.h
27  Comparison functions for floating point values.
28 */
29 
30 #include <cmath> // std::isnan, std::signbit, std::isinf
31 #include <cstdint> // int64_t, int32_t
32 #include <cstdlib> // std::abs
33 
35  private:
36  union Double_t {
38 
39  int64_t integer_part;
40  double double_value;
41  };
42 
43  union Float_t {
45 
46  int32_t integer_part;
47  float float_value;
48  };
49 
50  /**
51  Return the ULP difference between two floats.
52 
53  @param val1 first value
54  @param val2 second value
55 
56  @return the difference in ULPs between the two values
57  */
58  static int32_t get_ulp_diff(float val1, float val2) {
59  Float_t a(val1);
60  Float_t b(val2);
61 
62  return std::abs(a.integer_part - b.integer_part);
63  }
64 
65  /**
66  Return the ULP difference between two doubles.
67 
68  @param val1 first value
69  @param val2 second value
70 
71  @return the difference in ULPs between the two values
72  */
73  static int64_t get_ulp_diff(double val1, double val2) {
74  Double_t a(val1);
75  Double_t b(val2);
76 
77  return std::abs(a.integer_part - b.integer_part);
78  }
79 
80  public:
81  /*
82  Compare floating point values for "almost equal".
83 
84  The implementation is based ULPs (Units in the Last Place), which works
85  something like the following:
86 
87  A nice property of double values is that if they are interpreted (not
88  converted or cast) as an 64-bit integer, increasing the integer value by one
89  gives us the next representable double value. Thus, by interpreting two
90  double values aD, bD as 64-bit integers aI, bI, the absolute difference
91  between aI and bI tells us how many representable double values there are
92  between aD and bD.
93 
94  The above only works if aD and dB have the same sign. However, if their sign
95  is different we can safely assume that the two values aren't "almost equal".
96 
97  An interesting side-effect of the above is that the ULP difference between
98  DBL_MAX and INFINITY is only 1, since INFINITY is the next representable
99  double value after DBL_MAX. This means that DBL_MAX and INFINITY are
100  considered "almost equal" unless we have some special handling for INFINITY.
101 
102  By adjusting the allowed difference in ULPs, we can control how close the
103  numbers should be to compare as "almost equal". The default ULP difference
104  is set to 4, which is the same what googletest uses.
105 
106  For further reading, see:
107  https://randomascii.wordpress.com/2012/02/25/
108  comparing-floating-point-numbers-2012-edition/
109 
110  @param val1 first value
111  @param val2 second value
112  @param max_ulp_diff the maximum difference in ULPs between val1 and
113  val2
114 
115  @return true if the two values are considered to be almost equal
116  */
117  template <class T>
118  static bool almost_equal(T val1, T val2,
119  decltype(get_ulp_diff(val1,
120  val2)) max_ulp_diff = 4) {
121  /*
122  According to IEEE standard, any comparison involving a NaN must return
123  false.
124  */
125  if (std::isnan(val1) || std::isnan(val2)) return false;
126 
127  // If the numbers have different signs, they are not equal.
128  if (std::signbit(val1) != std::signbit(val2)) {
129  //...but check for equality, since we should consider -0.0 equal to 0.0.
130  return val1 == val2;
131  }
132 
133  // Infinity should not be equal to any other than itself.
134  if (std::isinf(val1) != std::isinf(val2)) return false;
135 
136  // Find the difference in ULPs.
137  const auto ulp_diff = get_ulp_diff(val1, val2);
138  if (ulp_diff < max_ulp_diff) return true;
139  return false;
140  }
141 };
142 
143 #endif
Definition: float_compare.h:34
static int32_t get_ulp_diff(float val1, float val2)
Return the ULP difference between two floats.
Definition: float_compare.h:58
static int64_t get_ulp_diff(double val1, double val2)
Return the ULP difference between two doubles.
Definition: float_compare.h:73
static bool almost_equal(T val1, T val2, decltype(get_ulp_diff(val1, val2)) max_ulp_diff=4)
Definition: float_compare.h:118
const string value("\"Value\"")
Definition: float_compare.h:36
int64_t integer_part
Definition: float_compare.h:39
double double_value
Definition: float_compare.h:40
Double_t(double value)
Definition: float_compare.h:37
Definition: float_compare.h:43
float float_value
Definition: float_compare.h:47
int32_t integer_part
Definition: float_compare.h:46
Float_t(float value)
Definition: float_compare.h:44