clamped_math_impl.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. #ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
  5. #define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include <climits>
  9. #include <cmath>
  10. #include <cstdlib>
  11. #include <limits>
  12. #include <type_traits>
  13. #include "base/numerics/checked_math.h"
  14. #include "base/numerics/safe_conversions.h"
  15. #include "base/numerics/safe_math_shared_impl.h"
  16. namespace base {
  17. namespace internal {
  18. template <typename T,
  19. typename std::enable_if<std::is_integral<T>::value &&
  20. std::is_signed<T>::value>::type* = nullptr>
  21. constexpr T SaturatedNegWrapper(T value) {
  22. return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
  23. ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
  24. ? NegateWrapper(value)
  25. : std::numeric_limits<T>::max())
  26. : ClampedNegFastOp<T>::Do(value);
  27. }
  28. template <typename T,
  29. typename std::enable_if<std::is_integral<T>::value &&
  30. !std::is_signed<T>::value>::type* = nullptr>
  31. constexpr T SaturatedNegWrapper(T value) {
  32. return T(0);
  33. }
  34. template <
  35. typename T,
  36. typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
  37. constexpr T SaturatedNegWrapper(T value) {
  38. return -value;
  39. }
  40. template <typename T,
  41. typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
  42. constexpr T SaturatedAbsWrapper(T value) {
  43. // The calculation below is a static identity for unsigned types, but for
  44. // signed integer types it provides a non-branching, saturated absolute value.
  45. // This works because SafeUnsignedAbs() returns an unsigned type, which can
  46. // represent the absolute value of all negative numbers of an equal-width
  47. // integer type. The call to IsValueNegative() then detects overflow in the
  48. // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
  49. // a signed integer value. If it is the overflow case, we end up subtracting
  50. // one from the unsigned result, thus saturating to numeric_limits<T>::max().
  51. return static_cast<T>(SafeUnsignedAbs(value) -
  52. IsValueNegative<T>(SafeUnsignedAbs(value)));
  53. }
  54. template <
  55. typename T,
  56. typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
  57. constexpr T SaturatedAbsWrapper(T value) {
  58. return value < 0 ? -value : value;
  59. }
  60. template <typename T, typename U, class Enable = void>
  61. struct ClampedAddOp {};
  62. template <typename T, typename U>
  63. struct ClampedAddOp<T,
  64. U,
  65. typename std::enable_if<std::is_integral<T>::value &&
  66. std::is_integral<U>::value>::type> {
  67. using result_type = typename MaxExponentPromotion<T, U>::type;
  68. template <typename V = result_type>
  69. static constexpr V Do(T x, U y) {
  70. if (ClampedAddFastOp<T, U>::is_supported)
  71. return ClampedAddFastOp<T, U>::template Do<V>(x, y);
  72. static_assert(std::is_same<V, result_type>::value ||
  73. IsTypeInRangeForNumericType<U, V>::value,
  74. "The saturation result cannot be determined from the "
  75. "provided types.");
  76. const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
  77. V result = {};
  78. return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
  79. ? result
  80. : saturated;
  81. }
  82. };
  83. template <typename T, typename U, class Enable = void>
  84. struct ClampedSubOp {};
  85. template <typename T, typename U>
  86. struct ClampedSubOp<T,
  87. U,
  88. typename std::enable_if<std::is_integral<T>::value &&
  89. std::is_integral<U>::value>::type> {
  90. using result_type = typename MaxExponentPromotion<T, U>::type;
  91. template <typename V = result_type>
  92. static constexpr V Do(T x, U y) {
  93. // TODO(jschuh) Make this "constexpr if" once we're C++17.
  94. if (ClampedSubFastOp<T, U>::is_supported)
  95. return ClampedSubFastOp<T, U>::template Do<V>(x, y);
  96. static_assert(std::is_same<V, result_type>::value ||
  97. IsTypeInRangeForNumericType<U, V>::value,
  98. "The saturation result cannot be determined from the "
  99. "provided types.");
  100. const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
  101. V result = {};
  102. return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
  103. ? result
  104. : saturated;
  105. }
  106. };
  107. template <typename T, typename U, class Enable = void>
  108. struct ClampedMulOp {};
  109. template <typename T, typename U>
  110. struct ClampedMulOp<T,
  111. U,
  112. typename std::enable_if<std::is_integral<T>::value &&
  113. std::is_integral<U>::value>::type> {
  114. using result_type = typename MaxExponentPromotion<T, U>::type;
  115. template <typename V = result_type>
  116. static constexpr V Do(T x, U y) {
  117. // TODO(jschuh) Make this "constexpr if" once we're C++17.
  118. if (ClampedMulFastOp<T, U>::is_supported)
  119. return ClampedMulFastOp<T, U>::template Do<V>(x, y);
  120. V result = {};
  121. const V saturated =
  122. CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
  123. return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
  124. ? result
  125. : saturated;
  126. }
  127. };
  128. template <typename T, typename U, class Enable = void>
  129. struct ClampedDivOp {};
  130. template <typename T, typename U>
  131. struct ClampedDivOp<T,
  132. U,
  133. typename std::enable_if<std::is_integral<T>::value &&
  134. std::is_integral<U>::value>::type> {
  135. using result_type = typename MaxExponentPromotion<T, U>::type;
  136. template <typename V = result_type>
  137. static constexpr V Do(T x, U y) {
  138. V result = {};
  139. if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
  140. return result;
  141. // Saturation goes to max, min, or NaN (if x is zero).
  142. return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
  143. : SaturationDefaultLimits<V>::NaN();
  144. }
  145. };
  146. template <typename T, typename U, class Enable = void>
  147. struct ClampedModOp {};
  148. template <typename T, typename U>
  149. struct ClampedModOp<T,
  150. U,
  151. typename std::enable_if<std::is_integral<T>::value &&
  152. std::is_integral<U>::value>::type> {
  153. using result_type = typename MaxExponentPromotion<T, U>::type;
  154. template <typename V = result_type>
  155. static constexpr V Do(T x, U y) {
  156. V result = {};
  157. return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
  158. ? result
  159. : x;
  160. }
  161. };
  162. template <typename T, typename U, class Enable = void>
  163. struct ClampedLshOp {};
  164. // Left shift. Non-zero values saturate in the direction of the sign. A zero
  165. // shifted by any value always results in zero.
  166. template <typename T, typename U>
  167. struct ClampedLshOp<T,
  168. U,
  169. typename std::enable_if<std::is_integral<T>::value &&
  170. std::is_integral<U>::value>::type> {
  171. using result_type = T;
  172. template <typename V = result_type>
  173. static constexpr V Do(T x, U shift) {
  174. static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
  175. if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
  176. // Shift as unsigned to avoid undefined behavior.
  177. V result = static_cast<V>(as_unsigned(x) << shift);
  178. // If the shift can be reversed, we know it was valid.
  179. if (BASE_NUMERICS_LIKELY(result >> shift == x))
  180. return result;
  181. }
  182. return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
  183. }
  184. };
  185. template <typename T, typename U, class Enable = void>
  186. struct ClampedRshOp {};
  187. // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
  188. template <typename T, typename U>
  189. struct ClampedRshOp<T,
  190. U,
  191. typename std::enable_if<std::is_integral<T>::value &&
  192. std::is_integral<U>::value>::type> {
  193. using result_type = T;
  194. template <typename V = result_type>
  195. static constexpr V Do(T x, U shift) {
  196. static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
  197. // Signed right shift is odd, because it saturates to -1 or 0.
  198. const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
  199. return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
  200. ? saturated_cast<V>(x >> shift)
  201. : saturated;
  202. }
  203. };
  204. template <typename T, typename U, class Enable = void>
  205. struct ClampedAndOp {};
  206. template <typename T, typename U>
  207. struct ClampedAndOp<T,
  208. U,
  209. typename std::enable_if<std::is_integral<T>::value &&
  210. std::is_integral<U>::value>::type> {
  211. using result_type = typename std::make_unsigned<
  212. typename MaxExponentPromotion<T, U>::type>::type;
  213. template <typename V>
  214. static constexpr V Do(T x, U y) {
  215. return static_cast<result_type>(x) & static_cast<result_type>(y);
  216. }
  217. };
  218. template <typename T, typename U, class Enable = void>
  219. struct ClampedOrOp {};
  220. // For simplicity we promote to unsigned integers.
  221. template <typename T, typename U>
  222. struct ClampedOrOp<T,
  223. U,
  224. typename std::enable_if<std::is_integral<T>::value &&
  225. std::is_integral<U>::value>::type> {
  226. using result_type = typename std::make_unsigned<
  227. typename MaxExponentPromotion<T, U>::type>::type;
  228. template <typename V>
  229. static constexpr V Do(T x, U y) {
  230. return static_cast<result_type>(x) | static_cast<result_type>(y);
  231. }
  232. };
  233. template <typename T, typename U, class Enable = void>
  234. struct ClampedXorOp {};
  235. // For simplicity we support only unsigned integers.
  236. template <typename T, typename U>
  237. struct ClampedXorOp<T,
  238. U,
  239. typename std::enable_if<std::is_integral<T>::value &&
  240. std::is_integral<U>::value>::type> {
  241. using result_type = typename std::make_unsigned<
  242. typename MaxExponentPromotion<T, U>::type>::type;
  243. template <typename V>
  244. static constexpr V Do(T x, U y) {
  245. return static_cast<result_type>(x) ^ static_cast<result_type>(y);
  246. }
  247. };
  248. template <typename T, typename U, class Enable = void>
  249. struct ClampedMaxOp {};
  250. template <typename T, typename U>
  251. struct ClampedMaxOp<
  252. T,
  253. U,
  254. typename std::enable_if<std::is_arithmetic<T>::value &&
  255. std::is_arithmetic<U>::value>::type> {
  256. using result_type = typename MaxExponentPromotion<T, U>::type;
  257. template <typename V = result_type>
  258. static constexpr V Do(T x, U y) {
  259. return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
  260. : saturated_cast<V>(y);
  261. }
  262. };
  263. template <typename T, typename U, class Enable = void>
  264. struct ClampedMinOp {};
  265. template <typename T, typename U>
  266. struct ClampedMinOp<
  267. T,
  268. U,
  269. typename std::enable_if<std::is_arithmetic<T>::value &&
  270. std::is_arithmetic<U>::value>::type> {
  271. using result_type = typename LowestValuePromotion<T, U>::type;
  272. template <typename V = result_type>
  273. static constexpr V Do(T x, U y) {
  274. return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
  275. : saturated_cast<V>(y);
  276. }
  277. };
  278. // This is just boilerplate that wraps the standard floating point arithmetic.
  279. // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
  280. #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
  281. template <typename T, typename U> \
  282. struct Clamped##NAME##Op< \
  283. T, U, \
  284. typename std::enable_if<std::is_floating_point<T>::value || \
  285. std::is_floating_point<U>::value>::type> { \
  286. using result_type = typename MaxExponentPromotion<T, U>::type; \
  287. template <typename V = result_type> \
  288. static constexpr V Do(T x, U y) { \
  289. return saturated_cast<V>(x OP y); \
  290. } \
  291. };
  292. BASE_FLOAT_ARITHMETIC_OPS(Add, +)
  293. BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
  294. BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
  295. BASE_FLOAT_ARITHMETIC_OPS(Div, /)
  296. #undef BASE_FLOAT_ARITHMETIC_OPS
  297. } // namespace internal
  298. } // namespace base
  299. #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_