blob: d8a3301a172a45a6417f718df361fe3bccc0859f [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001// Copyright 2021 The Dawn & Tint Authors
Ben Clayton0ce9ab02022-05-05 20:23:40 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
Ben Clayton0ce9ab02022-05-05 20:23:40 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
Ben Clayton0ce9ab02022-05-05 20:23:40 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Ben Clayton0ce9ab02022-05-05 20:23:40 +000027
Ben Claytoncd52f382023-08-07 13:11:08 +000028#ifndef SRC_TINT_LANG_CORE_NUMBER_H_
29#define SRC_TINT_LANG_CORE_NUMBER_H_
Ben Clayton0ce9ab02022-05-05 20:23:40 +000030
31#include <stdint.h>
Ben Claytoncf52af72022-06-29 17:09:11 +000032#include <cmath>
Ben Clayton41285aa2022-05-10 14:55:34 +000033#include <functional>
Ben Claytonc2eccfc2022-05-25 15:04:24 +000034#include <limits>
dan sinclair0a4e2a12022-06-18 14:34:00 +000035#include <optional>
Ben Claytonc2eccfc2022-05-25 15:04:24 +000036
dan sinclair22b4dd22023-07-21 00:40:07 +000037#include "src/tint/utils/macros/compiler.h"
38#include "src/tint/utils/result/result.h"
39#include "src/tint/utils/text/string_stream.h"
40#include "src/tint/utils/traits/traits.h"
Ben Claytonc2eccfc2022-05-25 15:04:24 +000041
42// Forward declaration
dan sinclairce6dffe2023-08-14 21:01:40 +000043namespace tint::core {
Ben Claytonc2eccfc2022-05-25 15:04:24 +000044/// Number wraps a integer or floating point number, enforcing explicit casting.
45template <typename T>
46struct Number;
dan sinclairce6dffe2023-08-14 21:01:40 +000047} // namespace tint::core
Ben Clayton0ce9ab02022-05-05 20:23:40 +000048
dan sinclairce6dffe2023-08-14 21:01:40 +000049namespace tint::core::detail {
Antonio Maioranoc20c5df2022-09-01 14:57:39 +000050/// Base template for IsNumber
51template <typename T>
52struct IsNumber : std::false_type {};
53
54/// Specialization for IsNumber
55template <typename T>
56struct IsNumber<Number<T>> : std::true_type {};
57
Zhaoming Jiang62bfd312022-05-13 12:01:11 +000058/// An empty structure used as a unique template type for Number when
59/// specializing for the f16 type.
60struct NumberKindF16 {};
Ben Claytonc2eccfc2022-05-25 15:04:24 +000061
62/// Helper for obtaining the underlying type for a Number.
63template <typename T>
64struct NumberUnwrapper {
65 /// When T is not a Number, then type defined to be T.
66 using type = T;
67};
68
69/// NumberUnwrapper specialization for Number<T>.
70template <typename T>
71struct NumberUnwrapper<Number<T>> {
72 /// The Number's underlying type.
73 using type = typename Number<T>::type;
74};
75
dan sinclairce6dffe2023-08-14 21:01:40 +000076} // namespace tint::core::detail
Zhaoming Jiang62bfd312022-05-13 12:01:11 +000077
dan sinclairce6dffe2023-08-14 21:01:40 +000078namespace tint::core {
Ben Clayton0ce9ab02022-05-05 20:23:40 +000079
Antonio Maioranoc20c5df2022-09-01 14:57:39 +000080/// Evaluates to true iff T is a Number
81template <typename T>
dan sinclairce6dffe2023-08-14 21:01:40 +000082constexpr bool IsNumber = tint::core::detail::IsNumber<T>::value;
Antonio Maioranoc20c5df2022-09-01 14:57:39 +000083
Antonio Maioranod060f362022-07-29 17:12:01 +000084/// Resolves to the underlying type for a Number.
85template <typename T>
dan sinclairce6dffe2023-08-14 21:01:40 +000086using UnwrapNumber = typename tint::core::detail::NumberUnwrapper<T>::type;
Antonio Maioranod060f362022-07-29 17:12:01 +000087
Antonio Maiorano3740ac62022-09-03 22:42:51 +000088/// Evaluates to true iff T or Number<T> is a floating-point type or is NumberKindF16.
Antonio Maiorano6b3f4aa2022-10-04 22:40:32 +000089template <typename T>
90constexpr bool IsFloatingPoint = std::is_floating_point_v<UnwrapNumber<T>> ||
dan sinclairce6dffe2023-08-14 21:01:40 +000091 std::is_same_v<UnwrapNumber<T>, tint::core::detail::NumberKindF16>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +000092
93/// Evaluates to true iff T or Number<T> is an integral type.
Antonio Maiorano11e25712022-09-06 18:40:33 +000094template <typename T>
95constexpr bool IsIntegral = std::is_integral_v<UnwrapNumber<T>>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +000096
97/// Evaluates to true iff T or Number<T> is a signed integer type.
Antonio Maiorano11e25712022-09-06 18:40:33 +000098template <typename T>
99constexpr bool IsSignedIntegral =
100 std::is_integral_v<UnwrapNumber<T>> && std::is_signed_v<UnwrapNumber<T>>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +0000101
102/// Evaluates to true iff T or Number<T> is an unsigned integer type.
Antonio Maiorano11e25712022-09-06 18:40:33 +0000103template <typename T>
104constexpr bool IsUnsignedIntegral =
105 std::is_integral_v<UnwrapNumber<T>> && std::is_unsigned_v<UnwrapNumber<T>>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +0000106
107/// Evaluates to true iff T is an integer type, floating-point type or is NumberKindF16.
108template <typename T>
109constexpr bool IsNumeric = IsIntegral<T> || IsFloatingPoint<T>;
110
Antonio Maiorano5f33fac2022-09-23 21:58:29 +0000111/// Returns the bit width of T
112template <typename T>
113constexpr size_t BitWidth = sizeof(UnwrapNumber<T>) * 8;
114
Antonio Maioranod060f362022-07-29 17:12:01 +0000115/// NumberBase is a CRTP base class for Number<T>
116template <typename NumberT>
117struct NumberBase {
118 /// @returns value of type `Number<T>` with the highest value for that type.
119 static NumberT Highest() { return NumberT(NumberT::kHighestValue); }
120 /// @returns value of type `Number<T>` with the lowest value for that type.
121 static NumberT Lowest() { return NumberT(NumberT::kLowestValue); }
122 /// @returns value of type `Number<T>` with the smallest value for that type.
123 static NumberT Smallest() { return NumberT(NumberT::kSmallestValue); }
124 /// @returns value of type `Number<T>` that represents NaN for that type.
125 static NumberT NaN() {
126 return NumberT(std::numeric_limits<UnwrapNumber<NumberT>>::quiet_NaN());
127 }
128 /// @returns value of type `Number<T>` that represents infinity for that type.
129 static NumberT Inf() { return NumberT(std::numeric_limits<UnwrapNumber<NumberT>>::infinity()); }
130};
131
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000132/// Number wraps a integer or floating point number, enforcing explicit casting.
133template <typename T>
Antonio Maioranod060f362022-07-29 17:12:01 +0000134struct Number : NumberBase<Number<T>> {
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000135 static_assert(IsNumeric<T>, "Number<T> constructed with non-numeric type");
136
137 /// type is the underlying type of the Number
138 using type = T;
139
Ben Clayton46ee6392022-11-09 22:04:11 +0000140 /// Number of bits in the number.
141 static constexpr size_t kNumBits = sizeof(T) * 8;
142
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000143 /// Highest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000144 static constexpr type kHighestValue = std::numeric_limits<type>::max();
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000145
146 /// Lowest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000147 static constexpr type kLowestValue = std::numeric_limits<type>::lowest();
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000148
149 /// Smallest positive normal value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000150 static constexpr type kSmallestValue =
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000151 std::is_integral_v<type> ? 0 : std::numeric_limits<type>::min();
152
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000153 /// Smallest positive subnormal value of this type, 0 for integral type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000154 static constexpr type kSmallestSubnormalValue =
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000155 std::is_integral_v<type> ? 0 : std::numeric_limits<type>::denorm_min();
156
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000157 /// Constructor. The value is zero-initialized.
158 Number() = default;
159
160 /// Constructor.
161 /// @param v the value to initialize this Number to
Ben Clayton41285aa2022-05-10 14:55:34 +0000162 template <typename U>
163 explicit Number(U v) : value(static_cast<T>(v)) {}
164
165 /// Constructor.
166 /// @param v the value to initialize this Number to
167 template <typename U>
168 explicit Number(Number<U> v) : value(static_cast<T>(v.value)) {}
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000169
170 /// Conversion operator
171 /// @returns the value as T
172 operator T() const { return value; }
173
Ben Clayton636e3d02022-05-10 16:02:06 +0000174 /// Negation operator
175 /// @returns the negative value of the number
176 Number operator-() const { return Number(-value); }
177
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000178 /// Assignment operator
179 /// @param v the new value
180 /// @returns this Number so calls can be chained
181 Number& operator=(T v) {
182 value = v;
183 return *this;
184 }
185
186 /// The number value
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000187 type value = {};
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000188};
189
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000190/// Writes the number to the ostream.
dan sinclair6cc183c2023-03-02 21:28:45 +0000191/// @param out the stream to write to
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000192/// @param num the Number
dan sinclair6cc183c2023-03-02 21:28:45 +0000193/// @return the stream so calls can be chained
Ben Clayton68919602023-07-28 22:51:18 +0000194template <typename STREAM, typename T, typename = traits::EnableIfIsOStream<STREAM>>
195auto& operator<<(STREAM& out, Number<T> num) {
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000196 return out << num.value;
197}
198
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000199/// The partial specification of Number for f16 type, storing the f16 value as float,
200/// and enforcing proper explicit casting.
201template <>
dan sinclairce6dffe2023-08-14 21:01:40 +0000202struct Number<tint::core::detail::NumberKindF16>
203 : NumberBase<Number<tint::core::detail::NumberKindF16>> {
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000204 /// C++ does not have a native float16 type, so we use a 32-bit float instead.
205 using type = float;
206
Ben Clayton46ee6392022-11-09 22:04:11 +0000207 /// Number of bits in the number.
208 static constexpr size_t kNumBits = 16;
209
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000210 /// Highest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000211 static constexpr type kHighestValue = 65504.0f; // 2¹⁵ × (1 + 1023/1024)
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000212
213 /// Lowest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000214 static constexpr type kLowestValue = -65504.0f;
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000215
216 /// Smallest positive normal value of this type.
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000217 /// binary16 0_00001_0000000000, value is 2⁻¹⁴.
Antonio Maioranod060f362022-07-29 17:12:01 +0000218 static constexpr type kSmallestValue = 0x1p-14f;
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000219
220 /// Smallest positive subnormal value of this type.
221 /// binary16 0_00000_0000000001, value is 2⁻¹⁴ * 2⁻¹⁰ = 2⁻²⁴.
Antonio Maioranod060f362022-07-29 17:12:01 +0000222 static constexpr type kSmallestSubnormalValue = 0x1p-24f;
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000223
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000224 /// Constructor. The value is zero-initialized.
225 Number() = default;
226
227 /// Constructor.
228 /// @param v the value to initialize this Number to
229 template <typename U>
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000230 explicit Number(U v) : value(Quantize(static_cast<type>(v))) {}
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000231
232 /// Constructor.
233 /// @param v the value to initialize this Number to
234 template <typename U>
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000235 explicit Number(Number<U> v) : value(Quantize(static_cast<type>(v.value))) {}
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000236
237 /// Conversion operator
238 /// @returns the value as the internal representation type of F16
239 operator float() const { return value; }
240
241 /// Negation operator
242 /// @returns the negative value of the number
dan sinclairce6dffe2023-08-14 21:01:40 +0000243 Number operator-() const { return Number<tint::core::detail::NumberKindF16>(-value); }
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000244
245 /// Assignment operator with parameter as native floating point type
246 /// @param v the new value
247 /// @returns this Number so calls can be chained
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000248 Number& operator=(type v) {
249 value = Quantize(v);
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000250 return *this;
251 }
252
Zhaoming Jiang2c7440a2022-07-07 03:29:11 +0000253 /// Get the binary16 bit pattern in type uint16_t of this value.
254 /// @returns the binary16 bit pattern, in type uint16_t, of the stored quantized f16 value. If
255 /// the value is NaN, the returned value will be 0x7e00u. If the value is positive infinity, the
256 /// returned value will be 0x7c00u. If the input value is negative infinity, the returned value
257 /// will be 0xfc00u.
258 uint16_t BitsRepresentation() const;
259
dan sinclair00d0fd52022-11-09 20:03:09 +0000260 /// Creates an f16 value from the uint16_t bit representation.
261 /// @param bits the bits to convert from
262 /// @returns the binary16 value based off the provided bit pattern.
dan sinclairce6dffe2023-08-14 21:01:40 +0000263 static Number<tint::core::detail::NumberKindF16> FromBits(uint16_t bits);
dan sinclair00d0fd52022-11-09 20:03:09 +0000264
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000265 /// @param value the input float32 value
266 /// @returns the float32 value quantized to the smaller float16 value, through truncation of the
267 /// mantissa bits (no rounding). If the float32 value is too large (positive or negative) to be
268 /// represented by a float16 value, then the returned value will be positive or negative
269 /// infinity.
270 static type Quantize(type value);
271
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000272 /// The number value, stored as float
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000273 type value = {};
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000274};
275
Ben Clayton508e4a52022-05-10 22:08:04 +0000276/// `AInt` is a type alias to `Number<int64_t>`.
277using AInt = Number<int64_t>;
278/// `AFloat` is a type alias to `Number<double>`.
279using AFloat = Number<double>;
280
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000281/// `i32` is a type alias to `Number<int32_t>`.
282using i32 = Number<int32_t>;
283/// `u32` is a type alias to `Number<uint32_t>`.
284using u32 = Number<uint32_t>;
Ben Clayton41285aa2022-05-10 14:55:34 +0000285/// `f32` is a type alias to `Number<float>`
286using f32 = Number<float>;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000287/// `f16` is a type alias to `Number<detail::NumberKindF16>`, which should be IEEE 754 binary16.
288/// However since C++ don't have native binary16 type, the value is stored as float.
dan sinclairce6dffe2023-08-14 21:01:40 +0000289using f16 = Number<tint::core::detail::NumberKindF16>;
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000290
David Neto27c6d982023-06-19 16:49:10 +0000291/// The algorithms in this module require support for infinity and quiet NaNs on
292/// floating point types.
293static_assert(std::numeric_limits<float>::has_infinity);
294static_assert(std::numeric_limits<float>::has_quiet_NaN);
295static_assert(std::numeric_limits<double>::has_infinity);
296static_assert(std::numeric_limits<double>::has_quiet_NaN);
297
dan sinclairbae54e72023-07-28 15:01:54 +0000298template <typename T, tint::traits::EnableIf<IsFloatingPoint<T>>* = nullptr>
dan sinclairefe9c492022-11-21 18:00:01 +0000299inline const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
300
Antonio Maiorano5f33fac2022-09-23 21:58:29 +0000301/// True iff T is an abstract number type
302template <typename T>
303constexpr bool IsAbstract = std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>;
304
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000305/// @returns the friendly name of Number type T
dan sinclairbae54e72023-07-28 15:01:54 +0000306template <typename T, tint::traits::EnableIf<IsNumber<T>>* = nullptr>
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000307const char* FriendlyName() {
308 if constexpr (std::is_same_v<T, AInt>) {
309 return "abstract-int";
310 } else if constexpr (std::is_same_v<T, AFloat>) {
311 return "abstract-float";
312 } else if constexpr (std::is_same_v<T, i32>) {
313 return "i32";
314 } else if constexpr (std::is_same_v<T, u32>) {
315 return "u32";
316 } else if constexpr (std::is_same_v<T, f32>) {
317 return "f32";
318 } else if constexpr (std::is_same_v<T, f16>) {
319 return "f16";
320 } else {
321 static_assert(!sizeof(T), "Unhandled type");
322 }
323}
324
Antonio Maiorano29aa6132022-09-07 19:34:44 +0000325/// @returns the friendly name of T when T is bool
dan sinclairbae54e72023-07-28 15:01:54 +0000326template <typename T, tint::traits::EnableIf<std::is_same_v<T, bool>>* = nullptr>
Antonio Maiorano29aa6132022-09-07 19:34:44 +0000327const char* FriendlyName() {
328 return "bool";
329}
330
Ben Clayton61537d32022-05-31 13:14:29 +0000331/// Enumerator of failure reasons when converting from one number to another.
332enum class ConversionFailure {
333 kExceedsPositiveLimit, // The value was too big (+'ve) to fit in the target type
334 kExceedsNegativeLimit, // The value was too big (-'ve) to fit in the target type
335};
336
337/// Writes the conversion failure message to the ostream.
dan sinclair6cc183c2023-03-02 21:28:45 +0000338/// @param out the stream to write to
Ben Clayton61537d32022-05-31 13:14:29 +0000339/// @param failure the ConversionFailure
dan sinclair6cc183c2023-03-02 21:28:45 +0000340/// @return the stream so calls can be chained
Ben Clayton68919602023-07-28 22:51:18 +0000341template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
342auto& operator<<(STREAM& out, ConversionFailure failure) {
343 switch (failure) {
344 case ConversionFailure::kExceedsPositiveLimit:
345 return out << "value exceeds positive limit for type";
346 case ConversionFailure::kExceedsNegativeLimit:
347 return out << "value exceeds negative limit for type";
348 }
349 return out << "<unknown>";
350}
Ben Clayton61537d32022-05-31 13:14:29 +0000351
352/// Converts a number from one type to another, checking that the value fits in the target type.
Ben Clayton0740cf82023-07-28 21:40:41 +0000353/// @param num the value to convert
Ben Clayton61537d32022-05-31 13:14:29 +0000354/// @returns the resulting value of the conversion, or a failure reason.
355template <typename TO, typename FROM>
dan sinclairbae54e72023-07-28 15:01:54 +0000356tint::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) {
Ben Clayton2b4df782022-06-29 11:31:41 +0000357 // Use the highest-precision integer or floating-point type to perform the comparisons.
358 using T = std::conditional_t<IsFloatingPoint<UnwrapNumber<TO>> || IsFloatingPoint<FROM>,
359 AFloat::type, AInt::type>;
Ben Clayton61537d32022-05-31 13:14:29 +0000360 const auto value = static_cast<T>(num.value);
Antonio Maioranod060f362022-07-29 17:12:01 +0000361 if (value > static_cast<T>(TO::kHighestValue)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000362 return ConversionFailure::kExceedsPositiveLimit;
363 }
Antonio Maioranod060f362022-07-29 17:12:01 +0000364 if (value < static_cast<T>(TO::kLowestValue)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000365 return ConversionFailure::kExceedsNegativeLimit;
366 }
367 return TO(value); // Success
368}
369
Ben Claytoncf52af72022-06-29 17:09:11 +0000370/// Equality operator.
371/// @param a the LHS number
372/// @param b the RHS number
David Neto96018e52023-08-12 04:16:37 +0000373/// @returns true if the numbers `a` and `b` are exactly equal.
374/// For floating point types, negative zero equals zero.
375/// IEEE 754 says "Comparison shall ignore the sign of zero (so +0 = -0)."
Ben Claytoncf52af72022-06-29 17:09:11 +0000376template <typename A, typename B>
377bool operator==(Number<A> a, Number<B> b) {
378 // Use the highest-precision integer or floating-point type to perform the comparisons.
379 using T =
380 std::conditional_t<IsFloatingPoint<A> || IsFloatingPoint<B>, AFloat::type, AInt::type>;
381 auto va = static_cast<T>(a.value);
382 auto vb = static_cast<T>(b.value);
Ben Claytoncf52af72022-06-29 17:09:11 +0000383 return std::equal_to<T>()(va, vb);
384}
385
386/// Inequality operator.
387/// @param a the LHS number
388/// @param b the RHS number
389/// @returns true if the numbers `a` and `b` are exactly unequal. Also considers sign bit.
390template <typename A, typename B>
391bool operator!=(Number<A> a, Number<B> b) {
392 return !(a == b);
393}
394
395/// Equality operator.
396/// @param a the LHS number
397/// @param b the RHS number
398/// @returns true if the numbers `a` and `b` are exactly equal.
399template <typename A, typename B>
400std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) {
401 return a == Number<B>(b);
402}
403
404/// Inequality operator.
405/// @param a the LHS number
406/// @param b the RHS number
407/// @returns true if the numbers `a` and `b` are exactly unequal.
408template <typename A, typename B>
409std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
410 return !(a == b);
411}
412
413/// Equality operator.
414/// @param a the LHS number
415/// @param b the RHS number
416/// @returns true if the numbers `a` and `b` are exactly equal.
417template <typename A, typename B>
418std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) {
419 return Number<A>(a) == b;
420}
421
422/// Inequality operator.
423/// @param a the LHS number
424/// @param b the RHS number
425/// @returns true if the numbers `a` and `b` are exactly unequal.
426template <typename A, typename B>
427std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
428 return !(a == b);
429}
430
Ben Clayton61537d32022-05-31 13:14:29 +0000431/// Define 'TINT_HAS_OVERFLOW_BUILTINS' if the compiler provide overflow checking builtins.
432/// If the compiler does not support these builtins, then these are emulated with algorithms
433/// described in:
434/// https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
435#if defined(__GNUC__) && __GNUC__ >= 5
436#define TINT_HAS_OVERFLOW_BUILTINS
437#elif defined(__clang__)
438#if __has_builtin(__builtin_add_overflow) && __has_builtin(__builtin_mul_overflow)
439#define TINT_HAS_OVERFLOW_BUILTINS
440#endif
441#endif
442
Ben Clayton0740cf82023-07-28 21:40:41 +0000443/// @param a the LHS number
444/// @param b the RHS number
Ben Clayton61537d32022-05-31 13:14:29 +0000445/// @returns a + b, or an empty optional if the resulting value overflowed the AInt
446inline std::optional<AInt> CheckedAdd(AInt a, AInt b) {
447 int64_t result;
448#ifdef TINT_HAS_OVERFLOW_BUILTINS
449 if (__builtin_add_overflow(a.value, b.value, &result)) {
450 return {};
451 }
452#else // TINT_HAS_OVERFLOW_BUILTINS
453 if (a.value >= 0) {
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000454 if (b.value > AInt::kHighestValue - a.value) {
Ben Clayton61537d32022-05-31 13:14:29 +0000455 return {};
456 }
457 } else {
Antonio Maioranod060f362022-07-29 17:12:01 +0000458 if (b.value < AInt::kLowestValue - a.value) {
Ben Clayton61537d32022-05-31 13:14:29 +0000459 return {};
460 }
461 }
462 result = a.value + b.value;
463#endif // TINT_HAS_OVERFLOW_BUILTINS
464 return AInt(result);
465}
466
Ben Clayton0740cf82023-07-28 21:40:41 +0000467/// @param a the LHS number
468/// @param b the RHS number
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000469/// @returns a + b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000470template <typename FloatingPointT,
dan sinclairbae54e72023-07-28 15:01:54 +0000471 typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000472inline std::optional<FloatingPointT> CheckedAdd(FloatingPointT a, FloatingPointT b) {
473 auto result = FloatingPointT{a.value + b.value};
474 if (!std::isfinite(result.value)) {
Antonio Maiorano50940ae2022-08-03 21:20:26 +0000475 return {};
476 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000477 return result;
Antonio Maiorano50940ae2022-08-03 21:20:26 +0000478}
479
Ben Clayton0740cf82023-07-28 21:40:41 +0000480/// @param a the LHS number
481/// @param b the RHS number
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000482/// @returns a - b, or an empty optional if the resulting value overflowed the AInt
483inline std::optional<AInt> CheckedSub(AInt a, AInt b) {
484 int64_t result;
485#ifdef TINT_HAS_OVERFLOW_BUILTINS
486 if (__builtin_sub_overflow(a.value, b.value, &result)) {
487 return {};
488 }
489#else // TINT_HAS_OVERFLOW_BUILTINS
490 if (b.value >= 0) {
491 if (a.value < AInt::kLowestValue + b.value) {
492 return {};
493 }
494 } else {
495 if (a.value > AInt::kHighestValue + b.value) {
496 return {};
497 }
498 }
499 result = a.value - b.value;
500#endif // TINT_HAS_OVERFLOW_BUILTINS
501 return AInt(result);
502}
503
Ben Clayton0740cf82023-07-28 21:40:41 +0000504/// @param a the LHS number
505/// @param b the RHS number
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000506/// @returns a + b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000507template <typename FloatingPointT,
dan sinclairbae54e72023-07-28 15:01:54 +0000508 typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000509inline std::optional<FloatingPointT> CheckedSub(FloatingPointT a, FloatingPointT b) {
510 auto result = FloatingPointT{a.value - b.value};
511 if (!std::isfinite(result.value)) {
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000512 return {};
513 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000514 return result;
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000515}
516
Ben Clayton0740cf82023-07-28 21:40:41 +0000517/// @param a the LHS number
518/// @param b the RHS number
Ben Clayton61537d32022-05-31 13:14:29 +0000519/// @returns a * b, or an empty optional if the resulting value overflowed the AInt
520inline std::optional<AInt> CheckedMul(AInt a, AInt b) {
521 int64_t result;
522#ifdef TINT_HAS_OVERFLOW_BUILTINS
523 if (__builtin_mul_overflow(a.value, b.value, &result)) {
524 return {};
525 }
526#else // TINT_HAS_OVERFLOW_BUILTINS
527 if (a > 0) {
528 if (b > 0) {
Antonio Maioranod060f362022-07-29 17:12:01 +0000529 if (a > (AInt::kHighestValue / b)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000530 return {};
531 }
532 } else {
Antonio Maioranod060f362022-07-29 17:12:01 +0000533 if (b < (AInt::kLowestValue / a)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000534 return {};
535 }
536 }
537 } else {
538 if (b > 0) {
Antonio Maioranod060f362022-07-29 17:12:01 +0000539 if (a < (AInt::kLowestValue / b)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000540 return {};
541 }
542 } else {
Antonio Maioranod060f362022-07-29 17:12:01 +0000543 if ((a != 0) && (b < (AInt::kHighestValue / a))) {
Ben Clayton61537d32022-05-31 13:14:29 +0000544 return {};
545 }
546 }
547 }
548 result = a.value * b.value;
549#endif // TINT_HAS_OVERFLOW_BUILTINS
550 return AInt(result);
551}
552
Ben Clayton0740cf82023-07-28 21:40:41 +0000553/// @param a the LHS number
554/// @param b the RHS number
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000555/// @returns a * b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000556template <typename FloatingPointT,
dan sinclairbae54e72023-07-28 15:01:54 +0000557 typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000558inline std::optional<FloatingPointT> CheckedMul(FloatingPointT a, FloatingPointT b) {
559 auto result = FloatingPointT{a.value * b.value};
560 if (!std::isfinite(result.value)) {
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000561 return {};
562 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000563 return result;
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000564}
565
Ben Clayton0740cf82023-07-28 21:40:41 +0000566/// @param a the LHS number
567/// @param b the RHS number
Antonio Maiorano1741f442022-09-03 21:31:23 +0000568/// @returns a / b, or an empty optional if the resulting value overflowed the AInt
569inline std::optional<AInt> CheckedDiv(AInt a, AInt b) {
570 if (b == 0) {
571 return {};
572 }
573
574 if (b == -1 && a == AInt::Lowest()) {
575 return {};
576 }
577
578 return AInt{a.value / b.value};
579}
580
Ben Clayton0740cf82023-07-28 21:40:41 +0000581/// @param a the LHS number
582/// @param b the RHS number
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000583/// @returns a / b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000584template <typename FloatingPointT,
dan sinclairbae54e72023-07-28 15:01:54 +0000585 typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000586inline std::optional<FloatingPointT> CheckedDiv(FloatingPointT a, FloatingPointT b) {
David Neto96018e52023-08-12 04:16:37 +0000587 if (b == FloatingPointT{0.0}) {
Antonio Maioranodeb2ec92023-03-24 18:44:02 +0000588 return {};
589 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000590 auto result = FloatingPointT{a.value / b.value};
591 if (!std::isfinite(result.value)) {
Antonio Maiorano1741f442022-09-03 21:31:23 +0000592 return {};
593 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000594 return result;
Antonio Maiorano1741f442022-09-03 21:31:23 +0000595}
596
Antonio Maioranocfea2202022-11-30 14:18:51 +0000597namespace detail {
Ben Clayton0740cf82023-07-28 21:40:41 +0000598/// @param e1 the LHS number
599/// @param e2 the RHS number
Antonio Maioranocfea2202022-11-30 14:18:51 +0000600/// @returns the remainder of e1 / e2
601template <typename T>
602inline T Mod(T e1, T e2) {
Antonio Maiorano875d1162022-12-05 17:16:15 +0000603 if constexpr (IsIntegral<T>) {
604 return e1 % e2;
605
606 } else {
607 return e1 - e2 * std::trunc(e1 / e2);
608 }
Antonio Maioranocfea2202022-11-30 14:18:51 +0000609}
610} // namespace detail
611
Ben Clayton0740cf82023-07-28 21:40:41 +0000612/// @param a the LHS number
613/// @param b the RHS number
Antonio Maioranocfea2202022-11-30 14:18:51 +0000614/// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the AInt
615inline std::optional<AInt> CheckedMod(AInt a, AInt b) {
616 if (b == 0) {
617 return {};
618 }
619
620 if (b == -1 && a == AInt::Lowest()) {
621 return {};
622 }
623
dan sinclairce6dffe2023-08-14 21:01:40 +0000624 return AInt{tint::core::detail::Mod(a.value, b.value)};
Antonio Maioranocfea2202022-11-30 14:18:51 +0000625}
626
Ben Clayton0740cf82023-07-28 21:40:41 +0000627/// @param a the LHS number
628/// @param b the RHS number
Antonio Maioranocfea2202022-11-30 14:18:51 +0000629/// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the
630/// float value
dan sinclair05590052023-04-19 16:52:46 +0000631template <typename FloatingPointT,
dan sinclairbae54e72023-07-28 15:01:54 +0000632 typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maioranocfea2202022-11-30 14:18:51 +0000633inline std::optional<FloatingPointT> CheckedMod(FloatingPointT a, FloatingPointT b) {
David Neto96018e52023-08-12 04:16:37 +0000634 if (b == FloatingPointT{0.0}) {
Antonio Maioranodeb2ec92023-03-24 18:44:02 +0000635 return {};
636 }
dan sinclairce6dffe2023-08-14 21:01:40 +0000637 auto result = FloatingPointT{tint::core::detail::Mod(a.value, b.value)};
Antonio Maioranocfea2202022-11-30 14:18:51 +0000638 if (!std::isfinite(result.value)) {
639 return {};
640 }
641 return result;
642}
643
Ben Clayton0740cf82023-07-28 21:40:41 +0000644/// @param a the LHS number of the multiply
645/// @param b the RHS number of the multiply
646/// @param c the RHS number of the addition
Ben Clayton61537d32022-05-31 13:14:29 +0000647/// @returns a * b + c, or an empty optional if the value overflowed the AInt
648inline std::optional<AInt> CheckedMadd(AInt a, AInt b, AInt c) {
Ben Clayton61537d32022-05-31 13:14:29 +0000649 if (auto mul = CheckedMul(a, b)) {
650 return CheckedAdd(mul.value(), c);
651 }
652 return {};
Ben Clayton61537d32022-05-31 13:14:29 +0000653}
654
Ben Clayton0740cf82023-07-28 21:40:41 +0000655/// @param base the base number of the exponent operation
656/// @param exp the exponent
Antonio Maiorano7f5b9d02022-12-13 16:29:06 +0000657/// @returns the value of `base` raised to the power `exp`, or an empty optional if the operation
658/// cannot be performed.
dan sinclair05590052023-04-19 16:52:46 +0000659template <typename FloatingPointT,
dan sinclairbae54e72023-07-28 15:01:54 +0000660 typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano7f5b9d02022-12-13 16:29:06 +0000661inline std::optional<FloatingPointT> CheckedPow(FloatingPointT base, FloatingPointT exp) {
662 static_assert(IsNumber<FloatingPointT>);
663 if ((base < 0) || (base == 0 && exp <= 0)) {
664 return {};
665 }
666 auto result = FloatingPointT{std::pow(base.value, exp.value)};
667 if (!std::isfinite(result.value)) {
668 return {};
669 }
670 return result;
671}
672
dan sinclairce6dffe2023-08-14 21:01:40 +0000673} // namespace tint::core
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000674
dan sinclairce6dffe2023-08-14 21:01:40 +0000675namespace tint::core::number_suffixes {
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000676
Ben Clayton508e4a52022-05-10 22:08:04 +0000677/// Literal suffix for abstract integer literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000678inline AInt operator""_a(unsigned long long int value) { // NOLINT
Ben Clayton508e4a52022-05-10 22:08:04 +0000679 return AInt(static_cast<int64_t>(value));
680}
681
682/// Literal suffix for abstract float literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000683inline AFloat operator""_a(long double value) { // NOLINT
Ben Clayton508e4a52022-05-10 22:08:04 +0000684 return AFloat(static_cast<double>(value));
685}
686
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000687/// Literal suffix for i32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000688inline i32 operator""_i(unsigned long long int value) { // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000689 return i32(static_cast<int32_t>(value));
690}
691
692/// Literal suffix for u32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000693inline u32 operator""_u(unsigned long long int value) { // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000694 return u32(static_cast<uint32_t>(value));
695}
696
Ben Clayton41285aa2022-05-10 14:55:34 +0000697/// Literal suffix for f32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000698inline f32 operator""_f(long double value) { // NOLINT
Ben Clayton41285aa2022-05-10 14:55:34 +0000699 return f32(static_cast<double>(value));
700}
701
702/// Literal suffix for f32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000703inline f32 operator""_f(unsigned long long int value) { // NOLINT
Ben Clayton41285aa2022-05-10 14:55:34 +0000704 return f32(static_cast<double>(value));
705}
706
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000707/// Literal suffix for f16 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000708inline f16 operator""_h(long double value) { // NOLINT
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000709 return f16(static_cast<double>(value));
710}
711
712/// Literal suffix for f16 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000713inline f16 operator""_h(unsigned long long int value) { // NOLINT
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000714 return f16(static_cast<double>(value));
715}
716
dan sinclairce6dffe2023-08-14 21:01:40 +0000717} // namespace tint::core::number_suffixes
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000718
Ben Clayton0c7c23b2022-08-31 23:51:48 +0000719namespace std {
720
721/// Custom std::hash specialization for tint::Number<T>
722template <typename T>
dan sinclairce6dffe2023-08-14 21:01:40 +0000723class hash<tint::core::Number<T>> {
Ben Clayton0c7c23b2022-08-31 23:51:48 +0000724 public:
725 /// @param n the Number
726 /// @return the hash value
dan sinclairce6dffe2023-08-14 21:01:40 +0000727 inline std::size_t operator()(const tint::core::Number<T>& n) const {
Ben Clayton0c7c23b2022-08-31 23:51:48 +0000728 return std::hash<decltype(n.value)>()(n.value);
729 }
730};
731
732} // namespace std
733
Ben Claytoncd52f382023-08-07 13:11:08 +0000734#endif // SRC_TINT_LANG_CORE_NUMBER_H_