// Copyright 2021 The Dawn & Tint Authors // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef SRC_TINT_LANG_CORE_NUMBER_H_ #define SRC_TINT_LANG_CORE_NUMBER_H_ #include <stdint.h> #include <cmath> #include <functional> #include <limits> #include <optional> #include "src/tint/utils/macros/compiler.h" #include "src/tint/utils/result/result.h" #include "src/tint/utils/text/string_stream.h" #include "src/tint/utils/traits/traits.h" // Forward declaration namespace tint::core { /// Number wraps a integer or floating point number, enforcing explicit casting. template <typename T> struct Number; } // namespace tint::core namespace tint::core::detail { /// Base template for IsNumber template <typename T> struct IsNumber : std::false_type { … }; /// Specialization for IsNumber IsNumber<Number<T>>; /// An empty structure used as a unique template type for Number when /// specializing for the f16 type. struct NumberKindF16 { … }; /// Helper for obtaining the underlying type for a Number. template <typename T> struct NumberUnwrapper { … }; /// NumberUnwrapper specialization for Number<T>. NumberUnwrapper<Number<T>>; } // namespace tint::core::detail namespace tint::core { /// Evaluates to true iff T is a Number IsNumber; /// Resolves to the underlying type for a Number. UnwrapNumber; /// Evaluates to true iff T or Number<T> is a floating-point type or is NumberKindF16. IsFloatingPoint; /// Evaluates to true iff T or Number<T> is an integral type. IsIntegral; /// Evaluates to true iff T or Number<T> is a signed integer type. IsSignedIntegral; /// Evaluates to true iff T or Number<T> is an unsigned integer type. IsUnsignedIntegral; /// Evaluates to true iff T is an integer type, floating-point type or is NumberKindF16. IsNumeric; /// Returns the bit width of T BitWidth; /// NumberBase is a CRTP base class for Number<T> template <typename NumberT> struct NumberBase { … }; /// Number wraps a integer or floating point number, enforcing explicit casting. template <typename T> struct Number : NumberBase<Number<T>> { … }; /// Writes the number to the ostream. /// @param out the stream to write to /// @param num the Number /// @return the stream so calls can be chained template <typename STREAM, typename T, typename = traits::EnableIfIsOStream<STREAM>> auto& operator<<(STREAM& out, Number<T> num) { … } /// The partial specification of Number for f16 type, storing the f16 value as float, /// and enforcing proper explicit casting. template <> struct Number<tint::core::detail::NumberKindF16> : NumberBase<Number<tint::core::detail::NumberKindF16>> { … }; /// `AInt` is a type alias to `Number<int64_t>`. AInt; /// `AFloat` is a type alias to `Number<double>`. AFloat; /// `i8` is a type alias to `Number<int8_t>`. i8; /// `i32` is a type alias to `Number<int32_t>`. i32; /// `u8` is a type alias to `Number<uint8_t>`. u8; /// `u32` is a type alias to `Number<uint32_t>`. u32; /// `f32` is a type alias to `Number<float>` f32; /// `f16` is a type alias to `Number<detail::NumberKindF16>`, which should be IEEE 754 binary16. /// However since C++ don't have native binary16 type, the value is stored as float. f16; /// The algorithms in this module require support for infinity and quiet NaNs on /// floating point types. static_assert …; static_assert …; static_assert …; static_assert …; kPi; /// True iff T is an abstract number type IsAbstract; /// @returns the friendly name of Number type T template <typename T, tint::traits::EnableIf<IsNumber<T>>* = nullptr> const char* FriendlyName() { … } /// @returns the friendly name of T when T is bool template <typename T, tint::traits::EnableIf<std::is_same_v<T, bool>>* = nullptr> const char* FriendlyName() { … } /// Enumerator of failure reasons when converting from one number to another. enum class ConversionFailure { … }; /// Writes the conversion failure message to the ostream. /// @param out the stream to write to /// @param failure the ConversionFailure /// @return the stream so calls can be chained template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>> auto& operator<<(STREAM& out, ConversionFailure failure) { … } /// Converts a number from one type to another, checking that the value fits in the target type. /// @param num the value to convert /// @returns the resulting value of the conversion, or a failure reason. template <typename TO, typename FROM> tint::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) { … } /// Equality operator. /// @param a the LHS number /// @param b the RHS number /// @returns true if the numbers `a` and `b` are exactly equal. /// For floating point types, negative zero equals zero. /// IEEE 754 says "Comparison shall ignore the sign of zero (so +0 = -0)." template <typename A, typename B> bool operator==(Number<A> a, Number<B> b) { … } /// Inequality operator. /// @param a the LHS number /// @param b the RHS number /// @returns true if the numbers `a` and `b` are exactly unequal. Also considers sign bit. template <typename A, typename B> bool operator!=(Number<A> a, Number<B> b) { … } /// Equality operator. /// @param a the LHS number /// @param b the RHS number /// @returns true if the numbers `a` and `b` are exactly equal. template <typename A, typename B> std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) { … } /// Inequality operator. /// @param a the LHS number /// @param b the RHS number /// @returns true if the numbers `a` and `b` are exactly unequal. template <typename A, typename B> std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) { … } /// Equality operator. /// @param a the LHS number /// @param b the RHS number /// @returns true if the numbers `a` and `b` are exactly equal. template <typename A, typename B> std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) { … } /// Inequality operator. /// @param a the LHS number /// @param b the RHS number /// @returns true if the numbers `a` and `b` are exactly unequal. template <typename A, typename B> std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) { … } /// Define 'TINT_HAS_OVERFLOW_BUILTINS' if the compiler provide overflow checking builtins. /// If the compiler does not support these builtins, then these are emulated with algorithms /// described in: /// https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow #if defined(__GNUC__) && __GNUC__ >= 5 #define TINT_HAS_OVERFLOW_BUILTINS #elif defined(__clang__) #if __has_builtin(__builtin_add_overflow) && __has_builtin(__builtin_mul_overflow) #define TINT_HAS_OVERFLOW_BUILTINS #endif #endif /// @param a the LHS number /// @param b the RHS number /// @returns a + b, or an empty optional if the resulting value overflowed the AInt inline std::optional<AInt> CheckedAdd(AInt a, AInt b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a + b, or an empty optional if the resulting value overflowed the float value template <typename FloatingPointT, typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>> inline std::optional<FloatingPointT> CheckedAdd(FloatingPointT a, FloatingPointT b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a - b, or an empty optional if the resulting value overflowed the AInt inline std::optional<AInt> CheckedSub(AInt a, AInt b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a + b, or an empty optional if the resulting value overflowed the float value template <typename FloatingPointT, typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>> inline std::optional<FloatingPointT> CheckedSub(FloatingPointT a, FloatingPointT b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a * b, or an empty optional if the resulting value overflowed the AInt inline std::optional<AInt> CheckedMul(AInt a, AInt b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a * b, or an empty optional if the resulting value overflowed the float value template <typename FloatingPointT, typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>> inline std::optional<FloatingPointT> CheckedMul(FloatingPointT a, FloatingPointT b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a / b, or an empty optional if the resulting value overflowed the AInt inline std::optional<AInt> CheckedDiv(AInt a, AInt b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns a / b, or an empty optional if the resulting value overflowed the float value template <typename FloatingPointT, typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>> inline std::optional<FloatingPointT> CheckedDiv(FloatingPointT a, FloatingPointT b) { … } namespace detail { /// @param e1 the LHS number /// @param e2 the RHS number /// @returns the remainder of e1 / e2 template <typename T> inline T Mod(T e1, T e2) { … } } // namespace detail /// @param a the LHS number /// @param b the RHS number /// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the AInt inline std::optional<AInt> CheckedMod(AInt a, AInt b) { … } /// @param a the LHS number /// @param b the RHS number /// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the /// float value template <typename FloatingPointT, typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>> inline std::optional<FloatingPointT> CheckedMod(FloatingPointT a, FloatingPointT b) { … } /// @param a the LHS number of the multiply /// @param b the RHS number of the multiply /// @param c the RHS number of the addition /// @returns a * b + c, or an empty optional if the value overflowed the AInt inline std::optional<AInt> CheckedMadd(AInt a, AInt b, AInt c) { … } /// @param base the base number of the exponent operation /// @param exp the exponent /// @returns the value of `base` raised to the power `exp`, or an empty optional if the operation /// cannot be performed. template <typename FloatingPointT, typename = tint::traits::EnableIf<IsFloatingPoint<FloatingPointT>>> inline std::optional<FloatingPointT> CheckedPow(FloatingPointT base, FloatingPointT exp) { … } } // namespace tint::core namespace tint::core::number_suffixes { /// Literal suffix for abstract integer literals inline AInt operator""_a(unsigned long long int value) { … } /// Literal suffix for abstract float literals inline AFloat operator""_a(long double value) { … } /// Literal suffix for i32 literals inline i32 operator""_i(unsigned long long int value) { … } /// Literal suffix for u32 literals inline u32 operator""_u(unsigned long long int value) { … } /// Literal suffix for f32 literals inline f32 operator""_f(long double value) { … } /// Literal suffix for f32 literals inline f32 operator""_f(unsigned long long int value) { … } /// Literal suffix for f16 literals inline f16 operator""_h(long double value) { … } /// Literal suffix for f16 literals inline f16 operator""_h(unsigned long long int value) { … } } // namespace tint::core::number_suffixes namespace std { /// Custom std::hash specialization for tint::Number<T> hash<tint::core::Number<T>>; } // namespace std #endif // SRC_TINT_LANG_CORE_NUMBER_H_