HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
numericCast.h
Go to the documentation of this file.
1 //
2 // Copyright 2024 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_GF_NUMERIC_CAST_H
8 #define PXR_BASE_GF_NUMERIC_CAST_H
9 
10 #include "pxr/pxr.h"
11 
12 #include "pxr/base/gf/traits.h"
13 
14 #include <cmath>
15 #include <limits>
16 #include <optional>
17 #include <type_traits>
18 
20 
21 /// Return true if integer \p t compares logically less-than integer \p u in a
22 /// mathematical sense. The comparison is safe against non-value-preserving
23 /// integral conversion.
24 ///
25 /// This mimics the C++20 std::cmp_less function for comparing integers of
26 /// different types where negative signed integers always compare less than (and
27 /// not equal to) unsigned integers.
28 template <class T, class U>
29 constexpr bool
30 GfIntegerCompareLess(T t, U u) noexcept
31 {
32  static_assert(std::is_integral_v<T> && std::is_integral_v<U>);
33 
34  if constexpr (std::is_signed_v<T> == std::is_signed_v<U>) {
35  return t < u;
36  }
37  else if constexpr (std::is_signed_v<T>) {
39  }
40  else {
41  return u >= 0 && t < std::make_unsigned_t<U>(u);
42  }
43 }
44 
46  GfNumericCastPosOverflow, ///< Value too high to convert.
47  GfNumericCastNegOverflow, ///< Value too low to convert.
48  GfNumericCastNaN ///< Value is a floating-point NaN.
49 };
50 
51 /// Attempt to convert \p from to a value of type \p To "safely". From and To
52 /// must be arithmetic types according to GfIsArithmetic -- either integral or
53 /// floating-point types (including GfHalf). Return a std::optional holding the
54 /// converted value if conversion succeeds, otherwise the empty optional. The
55 /// optional out-parameter \p failType can be used to determine why conversion
56 /// failed if desired.
57 ///
58 /// What "safely" means depends on the types From and To. If From and To are
59 /// both integral types, then \p from can safely convert to To if \p from is in
60 /// To's range. For example if \p from is an int32_t and To is uint16_t, then
61 /// \p from can successfully convert if it is in the range [0, 65535].
62 ///
63 /// If To is an integral type and From is a floating-point type (including
64 /// GfHalf), then \p from can safely convert to To if it is neither a NaN nor an
65 /// infinity, and after truncation to integer its value is in To's range, as
66 /// above.
67 ///
68 /// Following boost::numeric_cast's behavior, no range checking is performed
69 /// converting from integral to floating-point or from floating-point to other
70 /// floating-point types. Note that converting an integral value that is out of
71 /// GfHalf's _finite_ range will produce a +/- inf GfHalf.
72 ///
73 template <class To, class From>
74 std::optional<To>
75 GfNumericCast(From from, GfNumericCastFailureType *failType = nullptr)
76 {
77  static_assert(GfIsArithmetic<From>::value &&
79 
80  using FromLimits = std::numeric_limits<From>;
81  using ToLimits = std::numeric_limits<To>;
82 
83  auto setFail = [&failType](GfNumericCastFailureType ft) {
84  if (failType) {
85  *failType = ft;
86  };
87  };
88 
89  // int -> int.
90  if constexpr (std::is_integral_v<From> &&
91  std::is_integral_v<To>) {
92  // Range check integer to integer.
93  if (GfIntegerCompareLess(from, ToLimits::min())) {
94  setFail(GfNumericCastNegOverflow);
95  return {};
96  }
97  if (GfIntegerCompareLess(ToLimits::max(), from)) {
98  setFail(GfNumericCastPosOverflow);
99  return {};
100  }
101  // In-range.
102  return static_cast<To>(from);
103  }
104  // float -> int.
105  else if constexpr (GfIsFloatingPoint<From>::value &&
106  std::is_integral_v<To>) {
107  // If the floating point value is NaN we cannot convert.
108  if (std::isnan(from)) {
109  setFail(GfNumericCastNaN);
110  return {};
111  }
112  // If the floating point value is an infinity we cannot convert.
113  if (std::isinf(from)) {
114  setFail(std::signbit(static_cast<double>(from))
117  return {};
118  }
119  // Otherwise the floating point value must be (when truncated) in the
120  // range for the To type. We do this by mapping the low/high values for
121  // To into From, then displacing these away from zero by 1 to account
122  // for the truncation, then checking against this range. Note this
123  // works okay for GfHalf whose max is ~65,000 when converting to
124  // int32_t, say. In that case we get a range like (-inf, inf), meaning
125  // that all finite halfs are in-range.
126  From low = static_cast<From>(ToLimits::lowest()) - static_cast<From>(1);
127  From high = static_cast<From>(ToLimits::max()) + static_cast<From>(1);
128 
129  if (from <= low) {
130  setFail(GfNumericCastNegOverflow);
131  return {};
132  }
133  if (from >= high) {
134  setFail(GfNumericCastPosOverflow);
135  return {};
136  }
137  // The value is in-range.
138  return static_cast<To>(from);
139  }
140  // float -> float, or float -> int.
141  else {
142  (void)setFail; // hush compiler.
143 
144  // No range checking, following boost::numeric_cast.
145  return static_cast<To>(from);
146  }
147 }
148 
150 
151 #endif // PXR_BASE_GF_NUMERIC_CAST_H
GfNumericCastFailureType
Definition: numericCast.h:45
void
Definition: png.h:1083
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
Value too high to convert.
Definition: numericCast.h:46
FMT_INLINE FMT_CONSTEXPR bool signbit(T value)
Definition: format.h:2824
PXR_NAMESPACE_OPEN_SCOPE constexpr bool GfIntegerCompareLess(T t, U u) noexcept
Definition: numericCast.h:30
std::optional< To > GfNumericCast(From from, GfNumericCastFailureType *failType=nullptr)
Definition: numericCast.h:75
GLdouble t
Definition: glad.h:2397
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
Value is a floating-point NaN.
Definition: numericCast.h:48
Value too low to convert.
Definition: numericCast.h:47