Index: Source/wtf/MathExtras.h |
diff --git a/Source/wtf/MathExtras.h b/Source/wtf/MathExtras.h |
index 5925ca5e89a1d96dfb6fa34b9908ce9d10ee32f2..808ff9050d0774a024d8510d8e6e3f3cecf335e6 100644 |
--- a/Source/wtf/MathExtras.h |
+++ b/Source/wtf/MathExtras.h |
@@ -26,12 +26,13 @@ |
#ifndef WTF_MathExtras_h |
#define WTF_MathExtras_h |
+#include "wtf/Assertions.h" |
#include "wtf/CPU.h" |
+#include <algorithm> |
#include <cmath> |
#include <limits> |
#if COMPILER(MSVC) |
-#include "wtf/Assertions.h" |
#include <stdint.h> |
#endif |
@@ -222,54 +223,147 @@ inline float grad2rad(float g) { return g * piFloat / 200.0f; } |
inline float turn2grad(float t) { return t * 400; } |
inline float grad2turn(float g) { return g / 400; } |
-// std::numeric_limits<T>::min() returns the smallest positive value for floating point types |
-template<typename T> inline T defaultMinimumForClamp() { return std::numeric_limits<T>::min(); } |
-template<> inline float defaultMinimumForClamp() { return -std::numeric_limits<float>::max(); } |
-template<> inline double defaultMinimumForClamp() { return -std::numeric_limits<double>::max(); } |
-template<typename T> inline T defaultMaximumForClamp() { return std::numeric_limits<T>::max(); } |
+// clampTo() is implemented by templated helper classes (to allow for partial |
+// template specialization) as well as several helper functions. |
-template<typename T> inline T clampTo(double value, T min = defaultMinimumForClamp<T>(), T max = defaultMaximumForClamp<T>()) |
+// This helper function can be called when we know that: |
+// (1) The type signednesses match so the compiler will not produce signed vs. |
+// unsigned warnings |
+// (2) The default type promotions/conversions are sufficient to handle things |
+// correctly |
+template<typename LimitType, typename ValueType> inline LimitType clampToDirectComparison(ValueType value, LimitType min, LimitType max) |
{ |
- if (value >= static_cast<double>(max)) |
+ if (value >= max) |
return max; |
- if (value <= static_cast<double>(min)) |
- return min; |
- return static_cast<T>(value); |
+ return (value <= min) ? min : static_cast<LimitType>(value); |
} |
-template<> inline long long int clampTo(double, long long int, long long int); // clampTo does not support long long ints. |
-inline int clampToInteger(double value) |
-{ |
- return clampTo<int>(value); |
-} |
- |
-inline unsigned clampToUnsigned(double value) |
-{ |
- return clampTo<unsigned>(value); |
-} |
- |
-inline float clampToFloat(double value) |
-{ |
- return clampTo<float>(value); |
-} |
- |
-inline int clampToPositiveInteger(double value) |
-{ |
- return clampTo<int>(value, 0); |
-} |
+// For any floating-point limits, or integral limits smaller than long long, we |
+// can cast the limits to double without losing precision; then the only cases |
+// where |value| can't be represented accurately as a double are the ones where |
+// it's outside the limit range anyway. So doing all comparisons as doubles |
+// will give correct results. |
+// |
+// In some cases, we can get better performance by using |
+// clampToDirectComparison(). We use a templated class to switch between these |
+// two cases (instead of simply using a conditional within one function) in |
+// order to only compile the clampToDirectComparison() code for cases where it |
+// will actually be used; this prevents the compiler from emitting warnings |
+// about unsafe code (even though we wouldn't actually be executing that code). |
+template<bool canUseDirectComparison, typename LimitType, typename ValueType> class ClampToNonLongLongHelper; |
+template<typename LimitType, typename ValueType> class ClampToNonLongLongHelper<true, LimitType, ValueType> { |
+public: |
+ static inline LimitType clampTo(ValueType value, LimitType min, LimitType max) |
+ { |
+ return clampToDirectComparison(value, min, max); |
+ } |
+}; |
+ |
+template<typename LimitType, typename ValueType> class ClampToNonLongLongHelper<false, LimitType, ValueType> { |
+public: |
+ static inline LimitType clampTo(ValueType value, LimitType min, LimitType max) |
+ { |
+ const double doubleValue = static_cast<double>(value); |
+ if (doubleValue >= static_cast<double>(max)) |
+ return max; |
+ if (doubleValue <= static_cast<double>(min)) |
+ return min; |
+ // If the limit type is integer, we might get better performance by |
+ // casting |value| (as opposed to |doubleValue|) to the limit type. |
+ return std::numeric_limits<LimitType>::is_integer ? static_cast<LimitType>(value) : static_cast<LimitType>(doubleValue); |
+ } |
+}; |
+ |
+// The unspecialized version of this templated class handles clamping to |
+// anything other than [unsigned] long long int limits. It simply uses the |
+// class above to toggle between the "fast" and "safe" clamp implementations. |
+template<typename LimitType, typename ValueType> class ClampToHelper { |
+public: |
+ static inline LimitType clampTo(ValueType value, LimitType min, LimitType max) |
+ { |
+ // We only use clampToDirectComparison() when the integerness and |
+ // signedness of the two types matches. |
+ // |
+ // If the integerness of the types doesn't match, then at best |
+ // clampToDirectComparison() won't be much more efficient than the |
+ // cast-everything-to-double method, since we'll need to convert to |
+ // floating point anyway; at worst, we risk incorrect results when |
+ // clamping a float to a 32-bit integral type due to potential precision |
+ // loss. |
+ // |
+ // If the signedness doesn't match, clampToDirectComparison() will |
+ // produce warnings about comparing signed vs. unsigned, which are apt |
+ // since negative signed values will be converted to large unsigned ones |
+ // and we'll get incorrect results. |
+ return ClampToNonLongLongHelper<std::numeric_limits<LimitType>::is_integer == std::numeric_limits<ValueType>::is_integer && std::numeric_limits<LimitType>::is_signed == std::numeric_limits<ValueType>::is_signed, LimitType, ValueType>::clampTo(value, min, max); |
+ } |
+}; |
+ |
+// Clamping to [unsigned] long long int limits requires more care. These may |
+// not be accurately representable as doubles, so instead we cast |value| to the |
+// limit type. But that cast is undefined if |value| is floating point and |
+// outside the representable range of the limit type, so we also have to check |
+// for that case explicitly. |
+template<typename ValueType> class ClampToHelper<long long int, ValueType> { |
+public: |
+ static inline long long int clampTo(ValueType value, long long int min, long long int max) |
+ { |
+ if (!std::numeric_limits<ValueType>::is_integer) { |
+ if (value > 0) { |
+ if (static_cast<double>(value) >= static_cast<double>(std::numeric_limits<long long int>::max())) |
+ return max; |
+ } else if (static_cast<double>(value) <= static_cast<double>(std::numeric_limits<long long int>::min())) { |
+ return min; |
+ } |
+ } |
+ // Note: If |value| were unsigned long long int, it could be larger than |
+ // the largest long long int, and this code would be wrong; we handle |
+ // this case with a separate full specialization below. |
+ return clampToDirectComparison(static_cast<long long int>(value), min, max); |
+ } |
+}; |
+ |
+// This specialization handles the case where the above partial specialization |
+// would be potentially incorrect. |
+template<> class ClampToHelper<long long int, unsigned long long int> { |
+public: |
+ static inline long long int clampTo(unsigned long long int value, long long int min, long long int max) |
+ { |
+ if (max <= 0 || value >= static_cast<unsigned long long int>(max)) |
+ return max; |
+ const long long int longLongValue = static_cast<long long int>(value); |
+ return (longLongValue <= min) ? min : longLongValue; |
+ } |
+}; |
+ |
+// This is similar to the partial specialization that clamps to long long int, |
+// but because the lower-bound check is done for integer value types as well, we |
+// don't need a <unsigned long long int, long long int> full specialization. |
+template<typename ValueType> class ClampToHelper<unsigned long long int, ValueType> { |
+public: |
+ static inline unsigned long long int clampTo(ValueType value, unsigned long long int min, unsigned long long int max) |
+ { |
+ if (value <= 0) |
+ return min; |
+ if (!std::numeric_limits<ValueType>::is_integer) { |
+ if (static_cast<double>(value) >= static_cast<double>(std::numeric_limits<unsigned long long int>::max())) |
+ return max; |
+ } |
+ return clampToDirectComparison(static_cast<unsigned long long int>(value), min, max); |
+ } |
+}; |
-inline int clampToInteger(float value) |
-{ |
- return clampTo<int>(value); |
-} |
+// This basically reimplements C++11's std::numeric_limits<T>::lowest(). |
+template<typename T> inline T defaultMinimumForClamp() { return std::numeric_limits<T>::min(); } |
+template<> inline float defaultMinimumForClamp<float>() { return -std::numeric_limits<float>::max(); } |
+template<> inline double defaultMinimumForClamp<double>() { return -std::numeric_limits<double>::max(); } |
-inline int clampToInteger(unsigned x) |
+// And, finally, the actual function for people to call. |
+template<typename LimitType, typename ValueType> inline LimitType clampTo(ValueType value, LimitType min = defaultMinimumForClamp<LimitType>(), LimitType max = std::numeric_limits<LimitType>::max()) |
{ |
- const unsigned intMax = static_cast<unsigned>(std::numeric_limits<int>::max()); |
- |
- if (x >= intMax) |
- return std::numeric_limits<int>::max(); |
- return static_cast<int>(x); |
+ ASSERT(!std::isnan(static_cast<double>(value))); |
+ ASSERT(min <= max); // This also ensures |min| and |max| aren't NaN. |
+ return ClampToHelper<LimitType, ValueType>::clampTo(value, min, max); |
} |
inline bool isWithinIntRange(float x) |