Loading...
Searching...
No Matches
numericCast.h
1//
2// Copyright 2024 Pixar
3//
4// Licensed under the Apache License, Version 2.0 (the "Apache License")
5// with the following modification; you may not use this file except in
6// compliance with the Apache License and the following modification to it:
7// Section 6. Trademarks. is deleted and replaced with:
8//
9// 6. Trademarks. This License does not grant permission to use the trade
10// names, trademarks, service marks, or product names of the Licensor
11// and its affiliates, except as required to comply with Section 4(c) of
12// the License and to reproduce the content of the NOTICE file.
13//
14// You may obtain a copy of the Apache License at
15//
16// http://www.apache.org/licenses/LICENSE-2.0
17//
18// Unless required by applicable law or agreed to in writing, software
19// distributed under the Apache License with the above modification is
20// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21// KIND, either express or implied. See the Apache License for the specific
22// language governing permissions and limitations under the Apache License.
23//
24#ifndef PXR_BASE_GF_NUMERIC_CAST_H
25#define PXR_BASE_GF_NUMERIC_CAST_H
26
27#include "pxr/pxr.h"
28
29#include "pxr/base/gf/traits.h"
30
31#include <cmath>
32#include <limits>
33#include <optional>
34#include <type_traits>
35
36PXR_NAMESPACE_OPEN_SCOPE
37
45template <class T, class U>
46constexpr bool
47GfIntegerCompareLess(T t, U u) noexcept
48{
49 static_assert(std::is_integral_v<T> && std::is_integral_v<U>);
50
51 if constexpr (std::is_signed_v<T> == std::is_signed_v<U>) {
52 return t < u;
53 }
54 else if constexpr (std::is_signed_v<T>) {
55 return t < 0 || std::make_unsigned_t<T>(t) < u;
56 }
57 else {
58 return u >= 0 && t < std::make_unsigned_t<U>(u);
59 }
60}
61
62enum GfNumericCastFailureType {
63 GfNumericCastPosOverflow,
64 GfNumericCastNegOverflow,
65 GfNumericCastNaN
66};
67
90template <class To, class From>
91std::optional<To>
92GfNumericCast(From from, GfNumericCastFailureType *failType = nullptr)
93{
94 static_assert(GfIsArithmetic<From>::value &&
96
97 using FromLimits = std::numeric_limits<From>;
98 using ToLimits = std::numeric_limits<To>;
99
100 auto setFail = [&failType](GfNumericCastFailureType ft) {
101 if (failType) {
102 *failType = ft;
103 };
104 };
105
106 // int -> int.
107 if constexpr (std::is_integral_v<From> &&
108 std::is_integral_v<To>) {
109 // Range check integer to integer.
110 if (GfIntegerCompareLess(from, ToLimits::min())) {
111 setFail(GfNumericCastNegOverflow);
112 return {};
113 }
114 if (GfIntegerCompareLess(ToLimits::max(), from)) {
115 setFail(GfNumericCastPosOverflow);
116 return {};
117 }
118 // In-range.
119 return static_cast<To>(from);
120 }
121 // float -> int.
122 else if constexpr (GfIsFloatingPoint<From>::value &&
123 std::is_integral_v<To>) {
124 // If the floating point value is NaN we cannot convert.
125 if (std::isnan(from)) {
126 setFail(GfNumericCastNaN);
127 return {};
128 }
129 // If the floating point value is an infinity we cannot convert.
130 if (std::isinf(from)) {
131 setFail(std::signbit(static_cast<double>(from))
132 ? GfNumericCastNegOverflow
133 : GfNumericCastPosOverflow);
134 return {};
135 }
136 // Otherwise the floating point value must be (when truncated) in the
137 // range for the To type. We do this by mapping the low/high values for
138 // To into From, then displacing these away from zero by 1 to account
139 // for the truncation, then checking against this range. Note this
140 // works okay for GfHalf whose max is ~65,000 when converting to
141 // int32_t, say. In that case we get a range like (-inf, inf), meaning
142 // that all finite halfs are in-range.
143 From low = static_cast<From>(ToLimits::lowest()) - static_cast<From>(1);
144 From high = static_cast<From>(ToLimits::max()) + static_cast<From>(1);
145
146 if (from <= low) {
147 setFail(GfNumericCastNegOverflow);
148 return {};
149 }
150 if (from >= high) {
151 setFail(GfNumericCastPosOverflow);
152 return {};
153 }
154 // The value is in-range.
155 return static_cast<To>(from);
156 }
157 // float -> float, or float -> int.
158 else {
159 (void)setFail; // hush compiler.
160
161 // No range checking, following boost::numeric_cast.
162 return static_cast<To>(from);
163 }
164}
165
166PXR_NAMESPACE_CLOSE_SCOPE
167
168#endif // PXR_BASE_GF_NUMERIC_CAST_H
A metafunction which is equivalent to std::arithmetic but also includes any specializations from GfIs...
Definition: traits.h:68
A metafunction which is equivalent to std::is_floating_point but allows for additional specialization...
Definition: traits.h:62