OR-Tools  8.0
fp_utils.h
Go to the documentation of this file.
1 // Copyright 2010-2018 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 <algorithm>
36 #include <cmath>
37 #include <limits>
38 
39 #include "ortools/base/logging.h"
40 
41 #if defined(_MSC_VER)
42 static inline double isnan(double value) { return _isnan(value); }
43 static inline double round(double value) { return floor(value + 0.5); }
44 #elif defined(__APPLE__) || __GNUC__ >= 5
45 using std::isnan;
46 #endif
47 
48 namespace operations_research {
49 
50 // ScopedFloatingPointEnv is used to easily enable Floating-point exceptions.
51 // The initial state is automatically restored when the object is deleted.
52 //
53 // Note(user): For some reason, this causes an FPE exception to be triggered for
54 // unknown reasons when compiled in 32 bits. Because of this, we do not turn
55 // on FPE exception if ARCH_K8 is not defined.
56 //
57 // TODO(user): Make it work on 32 bits.
58 // TODO(user): Make it work on msvc, currently calls to _controlfp crash.
59 
61  public:
63 #if defined(_MSC_VER)
64  // saved_control_ = _controlfp(0, 0);
65 #elif defined(ARCH_K8)
66  CHECK_EQ(0, fegetenv(&saved_fenv_));
67 #endif
68  }
69 
71 #if defined(_MSC_VER)
72  // CHECK_EQ(saved_control_, _controlfp(saved_control_, 0xFFFFFFFF));
73 #elif defined(ARCH_K8)
74  CHECK_EQ(0, fesetenv(&saved_fenv_));
75 #endif
76  }
77 
78  void EnableExceptions(int excepts) {
79 #if defined(_MSC_VER)
80  // _controlfp(static_cast<unsigned int>(excepts), _MCW_EM);
81 #elif defined(ARCH_K8)
82  CHECK_EQ(0, fegetenv(&fenv_));
83  excepts &= FE_ALL_EXCEPT;
84 #if defined(__APPLE__)
85  fenv_.__control &= ~excepts;
86 #elif defined(__FreeBSD__)
87  fenv_.__x87.__control &= ~excepts;
88 #else // Linux
89  fenv_.__control_word &= ~excepts;
90 #endif
91  fenv_.__mxcsr &= ~(excepts << 7);
92  CHECK_EQ(0, fesetenv(&fenv_));
93 #endif
94  }
95 
96  private:
97 #if defined(_MSC_VER)
98  // unsigned int saved_control_;
99 #elif defined(ARCH_K8)
100  fenv_t fenv_;
101  mutable fenv_t saved_fenv_;
102 #endif
103 };
104 
105 template <typename FloatType>
106 inline bool IsPositiveOrNegativeInfinity(FloatType x) {
107  return x == std::numeric_limits<FloatType>::infinity() ||
108  x == -std::numeric_limits<FloatType>::infinity();
109 }
110 
111 // Tests whether x and y are close to one another using absolute and relative
112 // tolerances.
113 // Returns true if |x - y| <= a (with a being the absolute_tolerance).
114 // The above case is useful for values that are close to zero.
115 // Returns true if |x - y| <= max(|x|, |y|) * r. (with r being the relative
116 // tolerance.)
117 // The cases for infinities are treated separately to avoid generating NaNs.
118 template <typename FloatType>
119 bool AreWithinAbsoluteOrRelativeTolerances(FloatType x, FloatType y,
120  FloatType relative_tolerance,
121  FloatType absolute_tolerance) {
122  DCHECK_LE(0.0, relative_tolerance);
123  DCHECK_LE(0.0, absolute_tolerance);
124  DCHECK_GT(1.0, relative_tolerance);
126  return x == y;
127  }
128  const FloatType difference = fabs(x - y);
129  if (difference <= absolute_tolerance) {
130  return true;
131  }
132  const FloatType largest_magnitude = std::max(fabs(x), fabs(y));
133  return difference <= largest_magnitude * relative_tolerance;
134 }
135 
136 // Tests whether x and y are close to one another using an absolute tolerance.
137 // Returns true if |x - y| <= a (with a being the absolute_tolerance).
138 // The cases for infinities are treated separately to avoid generating NaNs.
139 template <typename FloatType>
140 bool AreWithinAbsoluteTolerance(FloatType x, FloatType y,
141  FloatType absolute_tolerance) {
142  DCHECK_LE(0.0, absolute_tolerance);
144  return x == y;
145  }
146  return fabs(x - y) <= absolute_tolerance;
147 }
148 
149 // Returns true if x is less than y or slighlty greater than y with the given
150 // absolute or relative tolerance.
151 template <typename FloatType>
152 bool IsSmallerWithinTolerance(FloatType x, FloatType y, FloatType tolerance) {
153  if (IsPositiveOrNegativeInfinity(y)) return x <= y;
154  return x <= y + tolerance * std::max(1.0, std::min(std::abs(x), std::abs(y)));
155 }
156 
157 // Returns true if x is within tolerance of any integer. Always returns
158 // false for x equal to +/- infinity.
159 template <typename FloatType>
160 inline bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance) {
161  DCHECK_LE(0.0, tolerance);
162  if (IsPositiveOrNegativeInfinity(x)) return false;
163  return std::abs(x - std::round(x)) <= tolerance;
164 }
165 
166 // Handy alternatives to EXPECT_NEAR(), using relative and absolute tolerance
167 // instead of relative tolerance only, and with a proper support for infinity.
168 // TODO(user): investigate moving this to ortools/base/ or some other place.
169 #define EXPECT_COMPARABLE(expected, obtained, epsilon) \
170  EXPECT_TRUE(operations_research::AreWithinAbsoluteOrRelativeTolerances( \
171  expected, obtained, epsilon, epsilon)) \
172  << obtained << " != expected value " << expected \
173  << " within epsilon = " << epsilon;
174 
175 #define EXPECT_NOTCOMPARABLE(expected, obtained, epsilon) \
176  EXPECT_FALSE(operations_research::AreWithinAbsoluteOrRelativeTolerances( \
177  expected, obtained, epsilon, epsilon)) \
178  << obtained << " == expected value " << expected \
179  << " within epsilon = " << epsilon;
180 
181 // Given an array of doubles, this computes a positive scaling factor such that
182 // the scaled doubles can then be rounded to integers with little or no loss of
183 // precision, and so that the L1 norm of these integers is <= max_sum. More
184 // precisely, the following formulas will hold (x[i] is input[i], for brevity):
185 // - For all i, |round(factor * x[i]) / factor - x[i]| <= error * |x[i]|
186 // - The sum over i of |round(factor * x[i])| <= max_sum.
187 //
188 // The algorithm tries to minimize "error" (which is the relative error for one
189 // coefficient). Note however than in really broken cases, the error might be
190 // infinity and the factor zero.
191 //
192 // Note on the algorithm:
193 // - It only uses factors of the form 2^n (i.e. ldexp(1.0, n)) for simplicity.
194 // - The error will be zero in many practical instances. For example, if x
195 // contains only integers with low magnitude; or if x contains doubles whose
196 // exponents cover a small range.
197 // - It chooses the factor as high as possible under the given constraints, as
198 // a result the numbers produced may be large. To balance this, we recommend
199 // to divide the scaled integers by their gcd() which will result in no loss
200 // of precision and will help in many practical cases.
201 //
202 // TODO(user): incorporate the gcd computation here? The issue is that I am
203 // not sure if I just do factor /= gcd that round(x * factor) will be the same.
204 void GetBestScalingOfDoublesToInt64(const std::vector<double>& input,
205  int64 max_absolute_sum,
206  double* scaling_factor,
207  double* max_relative_coeff_error);
208 
209 // Returns the scaling factor like above with the extra conditions:
210 // - The sum over i of min(0, round(factor * x[i])) >= -max_sum.
211 // - The sum over i of max(0, round(factor * x[i])) <= max_sum.
212 // For any possible values of the x[i] such that x[i] is in [lb[i], ub[i]].
213 double GetBestScalingOfDoublesToInt64(const std::vector<double>& input,
214  const std::vector<double>& lb,
215  const std::vector<double>& ub,
216  int64 max_absolute_sum);
217 // This computes:
218 //
219 // The max_relative_coeff_error, which is the maximum over all coeff of
220 // |round(factor * x[i]) / (factor * x[i]) - 1|.
221 //
222 // The max_scaled_sum_error which is a bound on the maximum difference between
223 // the exact scaled sum and the rounded one. One needs to divide this by
224 // scaling_factor to have the maximum absolute error on the original sum.
225 void ComputeScalingErrors(const std::vector<double>& input,
226  const std::vector<double>& lb,
227  const std::vector<double>& ub,
228  const double scaling_factor,
229  double* max_relative_coeff_error,
230  double* max_scaled_sum_error);
231 
232 // Returns the Greatest Common Divisor of the numbers
233 // round(fabs(x[i] * scaling_factor)). The numbers 0 are ignored and if they are
234 // all zero then the result is 1. Note that round(fabs()) is the same as
235 // fabs(round()) since the numbers are rounded away from zero.
236 int64 ComputeGcdOfRoundedDoubles(const std::vector<double>& x,
237  double scaling_factor);
238 
239 // Returns alpha * x + (1 - alpha) * y.
240 template <typename FloatType>
241 inline FloatType Interpolate(FloatType x, FloatType y, FloatType alpha) {
242  return alpha * x + (1 - alpha) * y;
243 }
244 
245 } // namespace operations_research
246 
247 #endif // OR_TOOLS_UTIL_FP_UTILS_H_
min
int64 min
Definition: alldiff_cst.cc:138
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::ComputeGcdOfRoundedDoubles
int64 ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
Definition: fp_utils.cc:189
operations_research::ScopedFloatingPointEnv::~ScopedFloatingPointEnv
~ScopedFloatingPointEnv()
Definition: fp_utils.h:70
logging.h
operations_research::AreWithinAbsoluteTolerance
bool AreWithinAbsoluteTolerance(FloatType x, FloatType y, FloatType absolute_tolerance)
Definition: fp_utils.h:140
value
int64 value
Definition: demon_profiler.cc:43
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::IsIntegerWithinTolerance
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
Definition: fp_utils.h:160
int64
int64_t int64
Definition: integral_types.h:34
operations_research::ScopedFloatingPointEnv::ScopedFloatingPointEnv
ScopedFloatingPointEnv()
Definition: fp_utils.h:62
operations_research::ComputeScalingErrors
void ComputeScalingErrors(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, double scaling_factor, double *max_relative_coeff_error, double *max_scaled_sum_error)
Definition: fp_utils.cc:159
operations_research::Interpolate
FloatType Interpolate(FloatType x, FloatType y, FloatType alpha)
Definition: fp_utils.h:241
operations_research::GetBestScalingOfDoublesToInt64
double GetBestScalingOfDoublesToInt64(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, int64 max_absolute_sum)
Definition: fp_utils.cc:168
input
static int input(yyscan_t yyscanner)
operations_research::IsPositiveOrNegativeInfinity
bool IsPositiveOrNegativeInfinity(FloatType x)
Definition: fp_utils.h:106
max_relative_coeff_error
double max_relative_coeff_error
Definition: sat/lp_utils.cc:280
operations_research::IsSmallerWithinTolerance
bool IsSmallerWithinTolerance(FloatType x, FloatType y, FloatType tolerance)
Definition: fp_utils.h:152
operations_research::ScopedFloatingPointEnv
Definition: fp_utils.h:60
operations_research::AreWithinAbsoluteOrRelativeTolerances
bool AreWithinAbsoluteOrRelativeTolerances(FloatType x, FloatType y, FloatType relative_tolerance, FloatType absolute_tolerance)
Definition: fp_utils.h:119
operations_research::ScopedFloatingPointEnv::EnableExceptions
void EnableExceptions(int excepts)
Definition: fp_utils.h:78