1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // Utility functions on IEEE floating-point numbers.
15 // Implemented on float, double, and long double.
16 //
17 // Also a placeholder for tools controlling and checking FPU rounding modes.
18 //
19 // IMPORTANT NOTICE: you need to compile your binary with -frounding-math if
20 // you want to use rounding modes.
21 
22 #ifndef OR_TOOLS_UTIL_FP_UTILS_H_
23 #define OR_TOOLS_UTIL_FP_UTILS_H_
24 
25 #if defined(_MSC_VER)
26 #pragma fenv_access(on)  // NOLINT
27 #else
28 #include <fenv.h>  // NOLINT
29 #endif
30 
31 #ifdef __SSE__
32 #include <xmmintrin.h>
33 #endif
34 
35 #include <stdlib.h>
36 
37 #include <algorithm>
38 #include <cmath>
39 #include <limits>
40 #include <vector>
41 
42 #include "ortools/base/integral_types.h"
43 #include "ortools/base/logging.h"
44 
45 #if defined(_MSC_VER)
isnan(double value)46 static inline double isnan(double value) { return _isnan(value); }
round(double value)47 static inline double round(double value) { return floor(value + 0.5); }
48 #elif defined(__APPLE__) || __GNUC__ >= 5
49 using std::isnan;
50 #endif
51 
52 namespace operations_research {
53 
54 // ScopedFloatingPointEnv is used to easily enable Floating-point exceptions.
55 // The initial state is automatically restored when the object is deleted.
56 //
57 // Note(user): For some reason, this causes an FPE exception to be triggered for
58 // unknown reasons when compiled in 32 bits. Because of this, we do not turn
59 // on FPE exception if __x86_64__ is not defined.
60 //
61 // TODO(user): Make it work on 32 bits.
62 // TODO(user): Make it work on msvc, currently calls to _controlfp crash.
63 
64 class ScopedFloatingPointEnv {
65  public:
ScopedFloatingPointEnv()66   ScopedFloatingPointEnv() {
67 #if defined(_MSC_VER)
68     // saved_control_ = _controlfp(0, 0);
69 #elif (defined(__GNUC__) || defined(__llvm__)) && defined(__x86_64__)
70     CHECK_EQ(0, fegetenv(&saved_fenv_));
71 #endif
72   }
73 
~ScopedFloatingPointEnv()74   ~ScopedFloatingPointEnv() {
75 #if defined(_MSC_VER)
76     // CHECK_EQ(saved_control_, _controlfp(saved_control_, 0xFFFFFFFF));
77 #elif defined(__x86_64__) && defined(__GLIBC__)
78     CHECK_EQ(0, fesetenv(&saved_fenv_));
79 #endif
80   }
81 
EnableExceptions(int excepts)82   void EnableExceptions(int excepts) {
83 #if defined(_MSC_VER)
84     // _controlfp(static_cast<unsigned int>(excepts), _MCW_EM);
85 #elif (defined(__GNUC__) || defined(__llvm__)) && defined(__x86_64__) && \
86     !defined(__ANDROID__)
87     CHECK_EQ(0, fegetenv(&fenv_));
88     excepts &= FE_ALL_EXCEPT;
89 #if defined(__APPLE__)
90     fenv_.__control &= ~excepts;
91 #elif defined(__FreeBSD__) || defined(__DragonFly__)
92     fenv_.__x87.__control &= ~excepts;
93 #else  // Linux
94     fenv_.__control_word &= ~excepts;
95 #endif
96     fenv_.__mxcsr &= ~(excepts << 7);
97     CHECK_EQ(0, fesetenv(&fenv_));
98 #endif
99   }
100 
101  private:
102 #if defined(_MSC_VER)
103   // unsigned int saved_control_;
104 #elif (defined(__GNUC__) || defined(__llvm__)) && defined(__x86_64__)
105   fenv_t fenv_;
106   mutable fenv_t saved_fenv_;
107 #endif
108 };
109 
110 template <typename FloatType>
IsPositiveOrNegativeInfinity(FloatType x)111 inline bool IsPositiveOrNegativeInfinity(FloatType x) {
112   return x == std::numeric_limits<FloatType>::infinity() ||
113          x == -std::numeric_limits<FloatType>::infinity();
114 }
115 
116 // Tests whether x and y are close to one another using absolute and relative
117 // tolerances.
118 // Returns true if |x - y| <= a (with a being the absolute_tolerance).
119 // The above case is useful for values that are close to zero.
120 // Returns true if |x - y| <= max(|x|, |y|) * r. (with r being the relative
121 //                                                tolerance.)
122 // The cases for infinities are treated separately to avoid generating NaNs.
123 template <typename FloatType>
AreWithinAbsoluteOrRelativeTolerances(FloatType x,FloatType y,FloatType relative_tolerance,FloatType absolute_tolerance)124 bool AreWithinAbsoluteOrRelativeTolerances(FloatType x, FloatType y,
125                                            FloatType relative_tolerance,
126                                            FloatType absolute_tolerance) {
127   DCHECK_LE(0.0, relative_tolerance);
128   DCHECK_LE(0.0, absolute_tolerance);
129   DCHECK_GT(1.0, relative_tolerance);
130   if (IsPositiveOrNegativeInfinity(x) || IsPositiveOrNegativeInfinity(y)) {
131     return x == y;
132   }
133   const FloatType difference = fabs(x - y);
134   if (difference <= absolute_tolerance) {
135     return true;
136   }
137   const FloatType largest_magnitude = std::max(fabs(x), fabs(y));
138   return difference <= largest_magnitude * relative_tolerance;
139 }
140 
141 // Tests whether x and y are close to one another using an absolute tolerance.
142 // Returns true if |x - y| <= a (with a being the absolute_tolerance).
143 // The cases for infinities are treated separately to avoid generating NaNs.
144 template <typename FloatType>
AreWithinAbsoluteTolerance(FloatType x,FloatType y,FloatType absolute_tolerance)145 bool AreWithinAbsoluteTolerance(FloatType x, FloatType y,
146                                 FloatType absolute_tolerance) {
147   DCHECK_LE(0.0, absolute_tolerance);
148   if (IsPositiveOrNegativeInfinity(x) || IsPositiveOrNegativeInfinity(y)) {
149     return x == y;
150   }
151   return fabs(x - y) <= absolute_tolerance;
152 }
153 
154 // Returns true if x is less than y or slighlty greater than y with the given
155 // absolute or relative tolerance.
156 template <typename FloatType>
IsSmallerWithinTolerance(FloatType x,FloatType y,FloatType tolerance)157 bool IsSmallerWithinTolerance(FloatType x, FloatType y, FloatType tolerance) {
158   if (IsPositiveOrNegativeInfinity(y)) return x <= y;
159   return x <= y + tolerance * std::max(1.0, std::min(std::abs(x), std::abs(y)));
160 }
161 
162 // Returns true if x is within tolerance of any integer.  Always returns
163 // false for x equal to +/- infinity.
164 template <typename FloatType>
IsIntegerWithinTolerance(FloatType x,FloatType tolerance)165 inline bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance) {
166   DCHECK_LE(0.0, tolerance);
167   if (IsPositiveOrNegativeInfinity(x)) return false;
168   return std::abs(x - std::round(x)) <= tolerance;
169 }
170 
171 // Handy alternatives to EXPECT_NEAR(), using relative and absolute tolerance
172 // instead of relative tolerance only, and with a proper support for infinity.
173 // TODO(user): investigate moving this to ortools/base/ or some other place.
174 #define EXPECT_COMPARABLE(expected, obtained, epsilon)                    \
175   EXPECT_TRUE(operations_research::AreWithinAbsoluteOrRelativeTolerances( \
176       expected, obtained, epsilon, epsilon))                              \
177       << obtained << " != expected value " << expected                    \
178       << " within epsilon = " << epsilon;
179 
180 #define EXPECT_NOTCOMPARABLE(expected, obtained, epsilon)                  \
181   EXPECT_FALSE(operations_research::AreWithinAbsoluteOrRelativeTolerances( \
182       expected, obtained, epsilon, epsilon))                               \
183       << obtained << " == expected value " << expected                     \
184       << " within epsilon = " << epsilon;
185 
186 // Given an array of doubles, this computes a positive scaling factor such that
187 // the scaled doubles can then be rounded to integers with little or no loss of
188 // precision, and so that the L1 norm of these integers is <= max_sum. More
189 // precisely, the following formulas will hold (x[i] is input[i], for brevity):
190 // - For all i, |round(factor * x[i]) / factor  - x[i]| <= error * |x[i]|
191 // - The sum over i of |round(factor * x[i])| <= max_sum.
192 //
193 // The algorithm tries to minimize "error" (which is the relative error for one
194 // coefficient). Note however than in really broken cases, the error might be
195 // infinity and the factor zero.
196 //
197 // Note on the algorithm:
198 // - It only uses factors of the form 2^n (i.e. ldexp(1.0, n)) for simplicity.
199 // - The error will be zero in many practical instances. For example, if x
200 //   contains only integers with low magnitude; or if x contains doubles whose
201 //   exponents cover a small range.
202 // - It chooses the factor as high as possible under the given constraints, as
203 //   a result the numbers produced may be large. To balance this, we recommend
204 //   to divide the scaled integers by their gcd() which will result in no loss
205 //   of precision and will help in many practical cases.
206 //
207 // TODO(user): incorporate the gcd computation here? The issue is that I am
208 // not sure if I just do factor /= gcd that round(x * factor) will be the same.
209 void GetBestScalingOfDoublesToInt64(const std::vector<double>& input,
210                                     int64_t max_absolute_sum,
211                                     double* scaling_factor,
212                                     double* max_relative_coeff_error);
213 
214 // Returns the scaling factor like above with the extra conditions:
215 //  -  The sum over i of min(0, round(factor * x[i])) >= -max_sum.
216 //  -  The sum over i of max(0, round(factor * x[i])) <= max_sum.
217 // For any possible values of the x[i] such that x[i] is in [lb[i], ub[i]].
218 double GetBestScalingOfDoublesToInt64(const std::vector<double>& input,
219                                       const std::vector<double>& lb,
220                                       const std::vector<double>& ub,
221                                       int64_t max_absolute_sum);
222 // This computes:
223 //
224 // The max_relative_coeff_error, which is the maximum over all coeff of
225 // |round(factor * x[i]) / (factor * x[i])  - 1|.
226 //
227 // The max_scaled_sum_error which is a bound on the maximum difference between
228 // the exact scaled sum and the rounded one. One needs to divide this by
229 // scaling_factor to have the maximum absolute error on the original sum.
230 void ComputeScalingErrors(const std::vector<double>& input,
231                           const std::vector<double>& lb,
232                           const std::vector<double>& ub,
233                           const double scaling_factor,
234                           double* max_relative_coeff_error,
235                           double* max_scaled_sum_error);
236 
237 // Returns the Greatest Common Divisor of the numbers
238 // round(fabs(x[i] * scaling_factor)). The numbers 0 are ignored and if they are
239 // all zero then the result is 1. Note that round(fabs()) is the same as
240 // fabs(round()) since the numbers are rounded away from zero.
241 int64_t ComputeGcdOfRoundedDoubles(const std::vector<double>& x,
242                                    double scaling_factor);
243 
244 // Returns alpha * x + (1 - alpha) * y.
245 template <typename FloatType>
Interpolate(FloatType x,FloatType y,FloatType alpha)246 inline FloatType Interpolate(FloatType x, FloatType y, FloatType alpha) {
247   return alpha * x + (1 - alpha) * y;
248 }
249 
250 // This is a fast implementation of the C99 function ilogb for normalized
251 // doubles with the caveat that it returns -1023 for zero, and 1024 for infinity
252 // an NaNs.
253 int fast_ilogb(const double value);
254 
255 // This is a fast implementation of the C99 function scalbn, with the caveat
256 // that it works on normalized numbers and if the result underflows, overflows,
257 // or is applied to a NaN or an +-infinity, the result is undefined behavior.
258 // Note that the version of the function that takes a reference, modifies the
259 // given value.
260 double fast_scalbn(const double value, const int exponent);
261 void fast_scalbn_inplace(double& mutable_value, const int exponent);
262 
263 }  // namespace operations_research
264 
265 #endif  // OR_TOOLS_UTIL_FP_UTILS_H_
266