Flutter Engine
The Flutter Engine
clamped_math_impl.h
Go to the documentation of this file.
1// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
6#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
7
8#include <climits>
9#include <cmath>
10#include <cstddef>
11#include <cstdint>
12#include <cstdlib>
13#include <limits>
14#include <type_traits>
15
19
20namespace base {
21namespace internal {
22
23template <typename T,
28 ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
32}
33
34template <typename T,
37constexpr T SaturatedNegWrapper(T value) {
38 return T(0);
39}
40
41template <
42 typename T,
44constexpr T SaturatedNegWrapper(T value) {
45 return -value;
46}
47
48template <typename T,
51 // The calculation below is a static identity for unsigned types, but for
52 // signed integer types it provides a non-branching, saturated absolute value.
53 // This works because SafeUnsignedAbs() returns an unsigned type, which can
54 // represent the absolute value of all negative numbers of an equal-width
55 // integer type. The call to IsValueNegative() then detects overflow in the
56 // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
57 // a signed integer value. If it is the overflow case, we end up subtracting
58 // one from the unsigned result, thus saturating to numeric_limits<T>::max().
59 return static_cast<T>(SafeUnsignedAbs(value) -
60 IsValueNegative<T>(SafeUnsignedAbs(value)));
61}
62
63template <
64 typename T,
66constexpr T SaturatedAbsWrapper(T value) {
67 return value < 0 ? -value : value;
68}
69
70template <typename T, typename U, class Enable = void>
71struct ClampedAddOp {};
72
73template <typename T, typename U>
75 U,
76 typename std::enable_if<std::is_integral<T>::value &&
77 std::is_integral<U>::value>::type> {
79 template <typename V = result_type>
80 static constexpr V Do(T x, U y) {
83
86 "The saturation result cannot be determined from the "
87 "provided types.");
88 const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
89 V result = {};
91 ? result
92 : saturated;
93 }
94};
95
96template <typename T, typename U, class Enable = void>
97struct ClampedSubOp {};
98
99template <typename T, typename U>
101 U,
102 typename std::enable_if<std::is_integral<T>::value &&
103 std::is_integral<U>::value>::type> {
105 template <typename V = result_type>
106 static constexpr V Do(T x, U y) {
107 // TODO(jschuh) Make this "constexpr if" once we're C++17.
109 return ClampedSubFastOp<T, U>::template Do<V>(x, y);
110
113 "The saturation result cannot be determined from the "
114 "provided types.");
115 const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
116 V result = {};
118 ? result
119 : saturated;
120 }
121};
122
123template <typename T, typename U, class Enable = void>
124struct ClampedMulOp {};
125
126template <typename T, typename U>
128 U,
129 typename std::enable_if<std::is_integral<T>::value &&
130 std::is_integral<U>::value>::type> {
132 template <typename V = result_type>
133 static constexpr V Do(T x, U y) {
134 // TODO(jschuh) Make this "constexpr if" once we're C++17.
136 return ClampedMulFastOp<T, U>::template Do<V>(x, y);
137
138 V result = {};
139 const V saturated =
140 CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
142 ? result
143 : saturated;
144 }
145};
146
147template <typename T, typename U, class Enable = void>
148struct ClampedDivOp {};
149
150template <typename T, typename U>
152 U,
153 typename std::enable_if<std::is_integral<T>::value &&
154 std::is_integral<U>::value>::type> {
156 template <typename V = result_type>
157 static constexpr V Do(T x, U y) {
158 V result = {};
160 return result;
161 // Saturation goes to max, min, or NaN (if x is zero).
162 return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
164 }
165};
166
167template <typename T, typename U, class Enable = void>
168struct ClampedModOp {};
169
170template <typename T, typename U>
172 U,
173 typename std::enable_if<std::is_integral<T>::value &&
174 std::is_integral<U>::value>::type> {
176 template <typename V = result_type>
177 static constexpr V Do(T x, U y) {
178 V result = {};
180 ? result
181 : x;
182 }
183};
184
185template <typename T, typename U, class Enable = void>
186struct ClampedLshOp {};
187
188// Left shift. Non-zero values saturate in the direction of the sign. A zero
189// shifted by any value always results in zero.
190template <typename T, typename U>
192 U,
193 typename std::enable_if<std::is_integral<T>::value &&
194 std::is_integral<U>::value>::type> {
195 using result_type = T;
196 template <typename V = result_type>
197 static constexpr V Do(T x, U shift) {
198 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
199 if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
200 // Shift as unsigned to avoid undefined behavior.
201 V result = static_cast<V>(as_unsigned(x) << shift);
202 // If the shift can be reversed, we know it was valid.
203 if (BASE_NUMERICS_LIKELY(result >> shift == x))
204 return result;
205 }
206 return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
207 }
208};
209
210template <typename T, typename U, class Enable = void>
211struct ClampedRshOp {};
212
213// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
214template <typename T, typename U>
216 U,
217 typename std::enable_if<std::is_integral<T>::value &&
218 std::is_integral<U>::value>::type> {
219 using result_type = T;
220 template <typename V = result_type>
221 static constexpr V Do(T x, U shift) {
222 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
223 // Signed right shift is odd, because it saturates to -1 or 0.
224 const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
226 ? saturated_cast<V>(x >> shift)
227 : saturated;
228 }
229};
230
231template <typename T, typename U, class Enable = void>
232struct ClampedAndOp {};
233
234template <typename T, typename U>
236 U,
237 typename std::enable_if<std::is_integral<T>::value &&
238 std::is_integral<U>::value>::type> {
239 using result_type = typename std::make_unsigned<
241 template <typename V>
242 static constexpr V Do(T x, U y) {
243 return static_cast<result_type>(x) & static_cast<result_type>(y);
244 }
245};
246
247template <typename T, typename U, class Enable = void>
248struct ClampedOrOp {};
249
250// For simplicity we promote to unsigned integers.
251template <typename T, typename U>
253 U,
254 typename std::enable_if<std::is_integral<T>::value &&
255 std::is_integral<U>::value>::type> {
256 using result_type = typename std::make_unsigned<
258 template <typename V>
259 static constexpr V Do(T x, U y) {
260 return static_cast<result_type>(x) | static_cast<result_type>(y);
261 }
262};
263
264template <typename T, typename U, class Enable = void>
265struct ClampedXorOp {};
266
267// For simplicity we support only unsigned integers.
268template <typename T, typename U>
270 U,
271 typename std::enable_if<std::is_integral<T>::value &&
272 std::is_integral<U>::value>::type> {
273 using result_type = typename std::make_unsigned<
275 template <typename V>
276 static constexpr V Do(T x, U y) {
277 return static_cast<result_type>(x) ^ static_cast<result_type>(y);
278 }
279};
280
281template <typename T, typename U, class Enable = void>
282struct ClampedMaxOp {};
283
284template <typename T, typename U>
286 T,
287 U,
288 typename std::enable_if<std::is_arithmetic<T>::value &&
289 std::is_arithmetic<U>::value>::type> {
291 template <typename V = result_type>
292 static constexpr V Do(T x, U y) {
293 return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
294 : saturated_cast<V>(y);
295 }
296};
297
298template <typename T, typename U, class Enable = void>
299struct ClampedMinOp {};
300
301template <typename T, typename U>
303 T,
304 U,
305 typename std::enable_if<std::is_arithmetic<T>::value &&
306 std::is_arithmetic<U>::value>::type> {
308 template <typename V = result_type>
309 static constexpr V Do(T x, U y) {
310 return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
311 : saturated_cast<V>(y);
312 }
313};
314
315// This is just boilerplate that wraps the standard floating point arithmetic.
316// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
317#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
318 template <typename T, typename U> \
319 struct Clamped##NAME##Op< \
320 T, U, \
321 typename std::enable_if<std::is_floating_point<T>::value || \
322 std::is_floating_point<U>::value>::type> { \
323 using result_type = typename MaxExponentPromotion<T, U>::type; \
324 template <typename V = result_type> \
325 static constexpr V Do(T x, U y) { \
326 return saturated_cast<V>(x OP y); \
327 } \
328 };
329
334
335#undef BASE_FLOAT_ARITHMETIC_OPS
336
337} // namespace internal
338} // namespace base
339
340#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
GLenum type
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)
uint8_t value
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
T __attribute__((ext_vector_type(N))) V
double y
double x
constexpr T NegateWrapper(T value)
constexpr bool IsValueNegative(T value)
constexpr std::make_unsigned< T >::type SafeUnsignedAbs(T value)
constexpr std::make_unsigned< typenamebase::internal::UnderlyingType< Src >::type >::type as_unsigned(const Src value)
constexpr T SaturatedNegWrapper(T value)
constexpr T SaturatedAbsWrapper(T value)
constexpr bool MustTreatAsConstexpr(const T v)
Definition: ref_ptr.h:256
#define T
Definition: precompiler.cc:65
#define V(name)
Definition: raw_object.h:125
#define BASE_NUMERICS_LIKELY(x)
static constexpr bool Test(const L lhs, const R rhs)
static constexpr bool Test(const L lhs, const R rhs)