Loading...
Searching...
No Matches
numericCast.h
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"
14
15#include <cmath>
16#include <limits>
17#include <optional>
18#include <type_traits>
19
20PXR_NAMESPACE_OPEN_SCOPE
21
29template <class T, class U>
30constexpr bool
31GfIntegerCompareLess(T t, U u) noexcept
32{
33 // XXX:
34 // On Visual Studio warnings C4018 (signed/unsigned mismatch) and
35 // C4804 (unsafe use of bool in operation) are emitted when this
36 // function is used with boolean values. Just disable this for now.
37#if defined(ARCH_COMPILER_MSVC)
38 ARCH_PRAGMA_PUSH
39 ARCH_PRAGMA(warning(disable:4018))
40 ARCH_PRAGMA(warning(disable:4804))
41#endif
42
43 static_assert(std::is_integral_v<T> && std::is_integral_v<U>);
44
45 if constexpr (std::is_signed_v<T> == std::is_signed_v<U>) {
46 return t < u;
47 }
48 else if constexpr (std::is_signed_v<T>) {
49 return t < 0 || std::make_unsigned_t<T>(t) < u;
50 }
51 else {
52 return u >= 0 && t < std::make_unsigned_t<U>(u);
53 }
54
55#if defined(ARCH_COMPILER_MSVC)
56 ARCH_PRAGMA_POP
57#endif
58}
59
60enum GfNumericCastFailureType {
61 GfNumericCastPosOverflow,
62 GfNumericCastNegOverflow,
63 GfNumericCastNaN
64};
65
88template <class To, class From>
89std::optional<To>
90GfNumericCast(From from, GfNumericCastFailureType *failType = nullptr)
91{
92 static_assert(GfIsArithmetic<From>::value &&
94
95 using FromLimits = std::numeric_limits<From>;
96 using ToLimits = std::numeric_limits<To>;
97
98 auto setFail = [&failType](GfNumericCastFailureType ft) {
99 if (failType) {
100 *failType = ft;
101 };
102 };
103
104 // int -> int.
105 if constexpr (std::is_integral_v<From> &&
106 std::is_integral_v<To>) {
107 // Range check integer to integer.
108 if (GfIntegerCompareLess(from, ToLimits::min())) {
109 setFail(GfNumericCastNegOverflow);
110 return {};
111 }
112 if (GfIntegerCompareLess(ToLimits::max(), from)) {
113 setFail(GfNumericCastPosOverflow);
114 return {};
115 }
116 // In-range.
117 return static_cast<To>(from);
118 }
119 // float -> int.
120 else if constexpr (GfIsFloatingPoint<From>::value &&
121 std::is_integral_v<To>) {
122 // If the floating point value is NaN we cannot convert.
123 if (std::isnan(from)) {
124 setFail(GfNumericCastNaN);
125 return {};
126 }
127 // If the floating point value is an infinity we cannot convert.
128 if (std::isinf(from)) {
129 setFail(std::signbit(static_cast<double>(from))
130 ? GfNumericCastNegOverflow
131 : GfNumericCastPosOverflow);
132 return {};
133 }
134 // Otherwise the floating point value must be (when truncated) in the
135 // range for the To type. We do this by mapping the low/high values for
136 // To into From, then displacing these away from zero by 1 to account
137 // for the truncation, then checking against this range. Note this
138 // works okay for GfHalf whose max is ~65,000 when converting to
139 // int32_t, say. In that case we get a range like (-inf, inf), meaning
140 // that all finite halfs are in-range.
141 From low = static_cast<From>(ToLimits::lowest()) - static_cast<From>(1);
142 From high = static_cast<From>(ToLimits::max()) + static_cast<From>(1);
143
144 if (from <= low) {
145 setFail(GfNumericCastNegOverflow);
146 return {};
147 }
148 if (from >= high) {
149 setFail(GfNumericCastPosOverflow);
150 return {};
151 }
152 // The value is in-range.
153 return static_cast<To>(from);
154 }
155 // float -> float, or float -> int.
156 else {
157 (void)setFail; // hush compiler.
158
159 // No range checking, following boost::numeric_cast.
160 return static_cast<To>(from);
161 }
162}
163
164PXR_NAMESPACE_CLOSE_SCOPE
165
166#endif // PXR_BASE_GF_NUMERIC_CAST_H
Pragmas for controlling compiler-specific behaviors.
A metafunction which is equivalent to std::arithmetic but also includes any specializations from GfIs...
Definition: traits.h:51
A metafunction which is equivalent to std::is_floating_point but allows for additional specialization...
Definition: traits.h:45