mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-10 05:59:07 +00:00
tint: Add more helpers to tint::Number
• Add tint::CheckedConvert for converting between Number values and checking that the value fits in the target type. • Quantize the f16 values. • Add tint::NumberUnwrapper<T> to obtain the underlying type of a number. • Add ostream '<<' operator. • Add inequality operators. Bug: tint:1504 Change-Id: I7afa64867a8df0e55ccee16de14ce6a93fbe1965 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91303 Reviewed-by: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
3c83be8a5b
commit
c2eccfc887
@ -360,6 +360,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||||||
"inspector/resource_binding.h",
|
"inspector/resource_binding.h",
|
||||||
"inspector/scalar.cc",
|
"inspector/scalar.cc",
|
||||||
"inspector/scalar.h",
|
"inspector/scalar.h",
|
||||||
|
"number.cc",
|
||||||
"number.h",
|
"number.h",
|
||||||
"program.cc",
|
"program.cc",
|
||||||
"program.h",
|
"program.h",
|
||||||
|
@ -239,6 +239,7 @@ set(TINT_LIB_SRCS
|
|||||||
inspector/resource_binding.h
|
inspector/resource_binding.h
|
||||||
inspector/scalar.cc
|
inspector/scalar.cc
|
||||||
inspector/scalar.h
|
inspector/scalar.h
|
||||||
|
number.cc
|
||||||
number.h
|
number.h
|
||||||
program_builder.cc
|
program_builder.cc
|
||||||
program_builder.h
|
program_builder.h
|
||||||
@ -748,21 +749,23 @@ if(TINT_BUILD_TESTS)
|
|||||||
diagnostic/diagnostic_test.cc
|
diagnostic/diagnostic_test.cc
|
||||||
diagnostic/formatter_test.cc
|
diagnostic/formatter_test.cc
|
||||||
diagnostic/printer_test.cc
|
diagnostic/printer_test.cc
|
||||||
|
number_test.cc
|
||||||
|
program_builder_test.cc
|
||||||
program_test.cc
|
program_test.cc
|
||||||
resolver/array_accessor_test.cc
|
resolver/array_accessor_test.cc
|
||||||
resolver/assignment_validation_test.cc
|
resolver/assignment_validation_test.cc
|
||||||
resolver/atomics_test.cc
|
resolver/atomics_test.cc
|
||||||
resolver/atomics_validation_test.cc
|
resolver/atomics_validation_test.cc
|
||||||
|
resolver/attribute_validation_test.cc
|
||||||
resolver/bitcast_validation_test.cc
|
resolver/bitcast_validation_test.cc
|
||||||
resolver/builtins_validation_test.cc
|
|
||||||
resolver/builtin_test.cc
|
resolver/builtin_test.cc
|
||||||
resolver/builtin_validation_test.cc
|
resolver/builtin_validation_test.cc
|
||||||
|
resolver/builtins_validation_test.cc
|
||||||
resolver/call_test.cc
|
resolver/call_test.cc
|
||||||
resolver/call_validation_test.cc
|
resolver/call_validation_test.cc
|
||||||
resolver/compound_assignment_validation_test.cc
|
resolver/compound_assignment_validation_test.cc
|
||||||
resolver/compound_statement_test.cc
|
resolver/compound_statement_test.cc
|
||||||
resolver/control_block_validation_test.cc
|
resolver/control_block_validation_test.cc
|
||||||
resolver/attribute_validation_test.cc
|
|
||||||
resolver/dependency_graph_test.cc
|
resolver/dependency_graph_test.cc
|
||||||
resolver/entry_point_validation_test.cc
|
resolver/entry_point_validation_test.cc
|
||||||
resolver/function_validation_test.cc
|
resolver/function_validation_test.cc
|
||||||
@ -815,8 +818,8 @@ if(TINT_BUILD_TESTS)
|
|||||||
sem/sem_struct_test.cc
|
sem/sem_struct_test.cc
|
||||||
sem/storage_texture_test.cc
|
sem/storage_texture_test.cc
|
||||||
sem/texture_test.cc
|
sem/texture_test.cc
|
||||||
sem/type_test.cc
|
|
||||||
sem/type_manager_test.cc
|
sem/type_manager_test.cc
|
||||||
|
sem/type_test.cc
|
||||||
sem/u32_test.cc
|
sem/u32_test.cc
|
||||||
sem/vector_test.cc
|
sem/vector_test.cc
|
||||||
source_test.cc
|
source_test.cc
|
||||||
|
59
src/tint/number.cc
Normal file
59
src/tint/number.cc
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2022 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/tint/number.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, ConversionFailure failure) {
|
||||||
|
switch (failure) {
|
||||||
|
case ConversionFailure::kExceedsPositiveLimit:
|
||||||
|
return out << "value exceeds positive limit for type";
|
||||||
|
case ConversionFailure::kExceedsNegativeLimit:
|
||||||
|
return out << "value exceeds negative limit for type";
|
||||||
|
case ConversionFailure::kTooSmall:
|
||||||
|
return out << "value is too small for type";
|
||||||
|
}
|
||||||
|
return out << "<unknown>";
|
||||||
|
}
|
||||||
|
|
||||||
|
f16::type f16::Quantize(f16::type value) {
|
||||||
|
if (value > kHighest) {
|
||||||
|
return std::numeric_limits<f16::type>::infinity();
|
||||||
|
}
|
||||||
|
if (value < kLowest) {
|
||||||
|
return -std::numeric_limits<f16::type>::infinity();
|
||||||
|
}
|
||||||
|
// Below value must be within the finite range of a f16.
|
||||||
|
uint32_t u32;
|
||||||
|
memcpy(&u32, &value, 4);
|
||||||
|
if ((u32 & 0x7fffffffu) == 0) { // ~sign
|
||||||
|
return value; // +/- zero
|
||||||
|
}
|
||||||
|
if ((u32 & 0x7f800000) == 0x7f800000) { // exponent all 1's
|
||||||
|
return value; // inf or nan
|
||||||
|
}
|
||||||
|
// f32 bits : 1 sign, 8 exponent, 23 mantissa
|
||||||
|
// f16 bits : 1 sign, 5 exponent, 10 mantissa
|
||||||
|
// Mask the value to preserve the sign, exponent and most-significant 10 mantissa bits.
|
||||||
|
u32 = u32 & 0xffffe000u;
|
||||||
|
memcpy(&value, &u32, 4);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint
|
@ -17,18 +17,72 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "src/tint/utils/result.h"
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
namespace tint {
|
||||||
|
/// Number wraps a integer or floating point number, enforcing explicit casting.
|
||||||
|
template <typename T>
|
||||||
|
struct Number;
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
namespace tint::detail {
|
namespace tint::detail {
|
||||||
/// An empty structure used as a unique template type for Number when
|
/// An empty structure used as a unique template type for Number when
|
||||||
/// specializing for the f16 type.
|
/// specializing for the f16 type.
|
||||||
struct NumberKindF16 {};
|
struct NumberKindF16 {};
|
||||||
|
|
||||||
|
/// Helper for obtaining the underlying type for a Number.
|
||||||
|
template <typename T>
|
||||||
|
struct NumberUnwrapper {
|
||||||
|
/// When T is not a Number, then type defined to be T.
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// NumberUnwrapper specialization for Number<T>.
|
||||||
|
template <typename T>
|
||||||
|
struct NumberUnwrapper<Number<T>> {
|
||||||
|
/// The Number's underlying type.
|
||||||
|
using type = typename Number<T>::type;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace tint::detail
|
} // namespace tint::detail
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
|
||||||
|
/// Evaluates to true iff T is a floating-point type or is NumberKindF16.
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool IsFloatingPoint =
|
||||||
|
std::is_floating_point_v<T> || std::is_same_v<T, detail::NumberKindF16>;
|
||||||
|
|
||||||
|
/// Evaluates to true iff T is an integer type.
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool IsInteger = std::is_integral_v<T>;
|
||||||
|
|
||||||
|
/// Evaluates to true iff T is an integer type, floating-point type or is NumberKindF16.
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool IsNumeric = IsInteger<T> || IsFloatingPoint<T>;
|
||||||
|
|
||||||
/// Number wraps a integer or floating point number, enforcing explicit casting.
|
/// Number wraps a integer or floating point number, enforcing explicit casting.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Number {
|
struct Number {
|
||||||
|
static_assert(IsNumeric<T>, "Number<T> constructed with non-numeric type");
|
||||||
|
|
||||||
|
/// type is the underlying type of the Number
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
/// Highest finite representable value of this type.
|
||||||
|
static constexpr type kHighest = std::numeric_limits<type>::max();
|
||||||
|
|
||||||
|
/// Lowest finite representable value of this type.
|
||||||
|
static constexpr type kLowest = std::numeric_limits<type>::lowest();
|
||||||
|
|
||||||
|
/// Smallest positive normal value of this type.
|
||||||
|
static constexpr type kSmallest =
|
||||||
|
std::is_integral_v<type> ? 0 : std::numeric_limits<type>::min();
|
||||||
|
|
||||||
/// Constructor. The value is zero-initialized.
|
/// Constructor. The value is zero-initialized.
|
||||||
Number() = default;
|
Number() = default;
|
||||||
|
|
||||||
@ -59,41 +113,139 @@ struct Number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The number value
|
/// The number value
|
||||||
T value = {};
|
type value = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Resolves to the underlying type for a Number.
|
||||||
|
template <typename T>
|
||||||
|
using UnwrapNumber = typename detail::NumberUnwrapper<T>::type;
|
||||||
|
|
||||||
|
/// Writes the number to the ostream.
|
||||||
|
/// @param out the std::ostream to write to
|
||||||
|
/// @param num the Number
|
||||||
|
/// @return the std::ostream so calls can be chained
|
||||||
|
template <typename T>
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, Number<T> num) {
|
||||||
|
return out << num.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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>
|
template <typename A, typename B>
|
||||||
bool operator==(Number<A> a, Number<B> b) {
|
bool operator==(Number<A> a, Number<B> b) {
|
||||||
using T = decltype(a.value + b.value);
|
using T = decltype(a.value + b.value);
|
||||||
return std::equal_to<T>()(a.value, b.value);
|
return std::equal_to<T>()(static_cast<T>(a.value), static_cast<T>(b.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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>
|
template <typename A, typename B>
|
||||||
bool operator==(Number<A> a, B b) {
|
bool operator!=(Number<A> a, Number<B> b) {
|
||||||
|
return !(a == 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) {
|
||||||
return a == Number<B>(b);
|
return 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>
|
template <typename A, typename B>
|
||||||
bool operator==(A a, Number<B> b) {
|
std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
|
||||||
|
return !(a == 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) {
|
||||||
return Number<A>(a) == b;
|
return Number<A>(a) == 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) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerator of failure reasons when converting from one number to another.
|
||||||
|
enum class ConversionFailure {
|
||||||
|
kExceedsPositiveLimit, // The value was too big (+'ve) to fit in the target type
|
||||||
|
kExceedsNegativeLimit, // The value was too big (-'ve) to fit in the target type
|
||||||
|
kTooSmall, // The value was too small to fit in the target type
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Writes the conversion failure message to the ostream.
|
||||||
|
/// @param out the std::ostream to write to
|
||||||
|
/// @param failure the ConversionFailure
|
||||||
|
/// @return the std::ostream so calls can be chained
|
||||||
|
std::ostream& operator<<(std::ostream& out, ConversionFailure failure);
|
||||||
|
|
||||||
|
/// Converts a number from one type to another, checking that the value fits in the target type.
|
||||||
|
/// @returns the resulting value of the conversion, or a failure reason.
|
||||||
|
template <typename TO, typename FROM>
|
||||||
|
utils::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) {
|
||||||
|
using T = decltype(UnwrapNumber<TO>() + num.value);
|
||||||
|
const auto value = static_cast<T>(num.value);
|
||||||
|
if (value > static_cast<T>(TO::kHighest)) {
|
||||||
|
return ConversionFailure::kExceedsPositiveLimit;
|
||||||
|
}
|
||||||
|
if (value < static_cast<T>(TO::kLowest)) {
|
||||||
|
return ConversionFailure::kExceedsNegativeLimit;
|
||||||
|
}
|
||||||
|
if constexpr (IsFloatingPoint<UnwrapNumber<TO>>) {
|
||||||
|
if ((value < T(0) && value > static_cast<T>(-TO::kSmallest)) ||
|
||||||
|
(value > T(0) && value < static_cast<T>(TO::kSmallest))) {
|
||||||
|
return ConversionFailure::kTooSmall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TO(value); // Success
|
||||||
|
}
|
||||||
|
|
||||||
/// The partial specification of Number for f16 type, storing the f16 value as float,
|
/// The partial specification of Number for f16 type, storing the f16 value as float,
|
||||||
/// and enforcing proper explicit casting.
|
/// and enforcing proper explicit casting.
|
||||||
template <>
|
template <>
|
||||||
struct Number<detail::NumberKindF16> {
|
struct Number<detail::NumberKindF16> {
|
||||||
|
/// C++ does not have a native float16 type, so we use a 32-bit float instead.
|
||||||
|
using type = float;
|
||||||
|
|
||||||
|
/// Highest finite representable value of this type.
|
||||||
|
static constexpr type kHighest = 65504.0f; // 2¹⁵ × (1 + 1023/1024)
|
||||||
|
|
||||||
|
/// Lowest finite representable value of this type.
|
||||||
|
static constexpr type kLowest = -65504.0f;
|
||||||
|
|
||||||
|
/// Smallest positive normal value of this type.
|
||||||
|
static constexpr type kSmallest = 0.00006103515625f; // 2⁻¹⁴
|
||||||
|
|
||||||
/// Constructor. The value is zero-initialized.
|
/// Constructor. The value is zero-initialized.
|
||||||
Number() = default;
|
Number() = default;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// @param v the value to initialize this Number to
|
/// @param v the value to initialize this Number to
|
||||||
template <typename U>
|
template <typename U>
|
||||||
explicit Number(U v) : value(static_cast<float>(v)) {}
|
explicit Number(U v) : value(Quantize(static_cast<type>(v))) {}
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// @param v the value to initialize this Number to
|
/// @param v the value to initialize this Number to
|
||||||
template <typename U>
|
template <typename U>
|
||||||
explicit Number(Number<U> v) : value(static_cast<float>(v.value)) {}
|
explicit Number(Number<U> v) : value(Quantize(static_cast<type>(v.value))) {}
|
||||||
|
|
||||||
/// Conversion operator
|
/// Conversion operator
|
||||||
/// @returns the value as the internal representation type of F16
|
/// @returns the value as the internal representation type of F16
|
||||||
@ -106,13 +258,20 @@ struct Number<detail::NumberKindF16> {
|
|||||||
/// Assignment operator with parameter as native floating point type
|
/// Assignment operator with parameter as native floating point type
|
||||||
/// @param v the new value
|
/// @param v the new value
|
||||||
/// @returns this Number so calls can be chained
|
/// @returns this Number so calls can be chained
|
||||||
Number& operator=(float v) {
|
Number& operator=(type v) {
|
||||||
value = v;
|
value = Quantize(v);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @param value the input float32 value
|
||||||
|
/// @returns the float32 value quantized to the smaller float16 value, through truncation of the
|
||||||
|
/// mantissa bits (no rounding). If the float32 value is too large (positive or negative) to be
|
||||||
|
/// represented by a float16 value, then the returned value will be positive or negative
|
||||||
|
/// infinity.
|
||||||
|
static type Quantize(type value);
|
||||||
|
|
||||||
/// The number value, stored as float
|
/// The number value, stored as float
|
||||||
float value = {};
|
type value = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `AInt` is a type alias to `Number<int64_t>`.
|
/// `AInt` is a type alias to `Number<int64_t>`.
|
||||||
|
145
src/tint/number_test.cc
Normal file
145
src/tint/number_test.cc
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright 2022 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "src/tint/program_builder.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int64_t kHighestI32 = static_cast<int64_t>(std::numeric_limits<int32_t>::max());
|
||||||
|
constexpr int64_t kHighestU32 = static_cast<int64_t>(std::numeric_limits<uint32_t>::max());
|
||||||
|
constexpr int64_t kLowestI32 = static_cast<int64_t>(std::numeric_limits<int32_t>::min());
|
||||||
|
constexpr int64_t kLowestU32 = static_cast<int64_t>(std::numeric_limits<uint32_t>::min());
|
||||||
|
|
||||||
|
// Highest float32 value. Calculated as:
|
||||||
|
// (2^127)×(1+(0x7fffff÷0x800000))
|
||||||
|
constexpr double kHighestF32 = 340282346638528859811704183484516925440.0;
|
||||||
|
|
||||||
|
// Next ULP up from kHighestF32 for a float64. Calculated as:
|
||||||
|
// (2^127)×(1+(0xfffffe0000001÷0x10000000000000))
|
||||||
|
constexpr double kHighestF32NextULP = 340282346638528897590636046441678635008.0;
|
||||||
|
|
||||||
|
// Smallest positive normal float32 value. Calculated as:
|
||||||
|
// 2^-126
|
||||||
|
constexpr double kSmallestF32 = 1.1754943508222875e-38;
|
||||||
|
|
||||||
|
// Next ULP down from kSmallestF32 for a float64. Calculated as:
|
||||||
|
// (2^-127)×(1+(0xfffffffffffff÷0x10000000000000))
|
||||||
|
constexpr double kSmallestF32PrevULP = 1.1754943508222874e-38;
|
||||||
|
|
||||||
|
// Highest float16 value. Calculated as:
|
||||||
|
// (2^15)×(1+(0x3ff÷0x400))
|
||||||
|
constexpr double kHighestF16 = 65504.0;
|
||||||
|
|
||||||
|
// Next ULP up from kHighestF16 for a float64. Calculated as:
|
||||||
|
// (2^15)×(1+(0xffc0000000001÷0x10000000000000))
|
||||||
|
constexpr double kHighestF16NextULP = 65504.00000000001;
|
||||||
|
|
||||||
|
// Smallest positive normal float16 value. Calculated as:
|
||||||
|
// 2^-14
|
||||||
|
constexpr double kSmallestF16 = 0.00006103515625;
|
||||||
|
|
||||||
|
// Next ULP down from kSmallestF16 for a float64. Calculated as:
|
||||||
|
// (2^-15)×(1+(0xfffffffffffff÷0x10000000000000))
|
||||||
|
constexpr double kSmallestF16PrevULP = 0.00006103515624999999;
|
||||||
|
|
||||||
|
constexpr double kLowestF32 = -kHighestF32;
|
||||||
|
constexpr double kLowestF32NextULP = -kHighestF32NextULP;
|
||||||
|
constexpr double kLowestF16 = -kHighestF16;
|
||||||
|
constexpr double kLowestF16NextULP = -kHighestF16NextULP;
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertIdentity) {
|
||||||
|
EXPECT_EQ(CheckedConvert<AInt>(0_a), 0_a);
|
||||||
|
EXPECT_EQ(CheckedConvert<AFloat>(0_a), 0.0_a);
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(0_i), 0_i);
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(0_u), 0_u);
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(0_f), 0_f);
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(0_h), 0_h);
|
||||||
|
|
||||||
|
EXPECT_EQ(CheckedConvert<AInt>(1_a), 1_a);
|
||||||
|
EXPECT_EQ(CheckedConvert<AFloat>(1_a), 1.0_a);
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(1_i), 1_i);
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(1_u), 1_u);
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(1_f), 1_f);
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(1_h), 1_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertLargestValue) {
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(AInt(kHighestI32)), i32(kHighestI32));
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(AInt(kHighestU32)), u32(kHighestU32));
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32)), f32(kHighestF32));
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16)), f16(kHighestF16));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertLowestValue) {
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(AInt(kLowestI32)), i32(kLowestI32));
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(AInt(kLowestU32)), u32(kLowestU32));
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(AFloat(kLowestF32)), f32(kLowestF32));
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(AFloat(kLowestF16)), f16(kLowestF16));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertSmallestValue) {
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(AInt(0)), i32(0));
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(AInt(0)), u32(0));
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(AFloat(kSmallestF32)), f32(kSmallestF32));
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(AFloat(kSmallestF16)), f16(kSmallestF16));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertExceedsPositiveLimit) {
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(AInt(kHighestI32 + 1)), ConversionFailure::kExceedsPositiveLimit);
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(AInt(kHighestU32 + 1)), ConversionFailure::kExceedsPositiveLimit);
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32NextULP)),
|
||||||
|
ConversionFailure::kExceedsPositiveLimit);
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16NextULP)),
|
||||||
|
ConversionFailure::kExceedsPositiveLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertExceedsNegativeLimit) {
|
||||||
|
EXPECT_EQ(CheckedConvert<i32>(AInt(kLowestI32 - 1)), ConversionFailure::kExceedsNegativeLimit);
|
||||||
|
EXPECT_EQ(CheckedConvert<u32>(AInt(kLowestU32 - 1)), ConversionFailure::kExceedsNegativeLimit);
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(AFloat(kLowestF32NextULP)),
|
||||||
|
ConversionFailure::kExceedsNegativeLimit);
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(AFloat(kLowestF16NextULP)),
|
||||||
|
ConversionFailure::kExceedsNegativeLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, CheckedConvertTooSmall) {
|
||||||
|
EXPECT_EQ(CheckedConvert<f32>(AFloat(kSmallestF32PrevULP)), ConversionFailure::kTooSmall);
|
||||||
|
EXPECT_EQ(CheckedConvert<f16>(AFloat(kSmallestF16PrevULP)), ConversionFailure::kTooSmall);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NumberTest, QuantizeF16) {
|
||||||
|
constexpr float nan = std::numeric_limits<float>::quiet_NaN();
|
||||||
|
constexpr float inf = std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
|
EXPECT_EQ(f16(0.0), 0.0f);
|
||||||
|
EXPECT_EQ(f16(1.0), 1.0f);
|
||||||
|
EXPECT_EQ(f16(0.00006106496), 0.000061035156f);
|
||||||
|
EXPECT_EQ(f16(1.0004883), 1.0f);
|
||||||
|
EXPECT_EQ(f16(-8196), -8192.f);
|
||||||
|
EXPECT_EQ(f16(65504.003), inf);
|
||||||
|
EXPECT_EQ(f16(-65504.003), -inf);
|
||||||
|
EXPECT_EQ(f16(inf), inf);
|
||||||
|
EXPECT_EQ(f16(-inf), -inf);
|
||||||
|
EXPECT_TRUE(std::isnan(f16(nan)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint
|
@ -727,6 +727,7 @@ tint_unittests_source_set("tint_unittests_core_src") {
|
|||||||
"../../src/tint/clone_context_test.cc",
|
"../../src/tint/clone_context_test.cc",
|
||||||
"../../src/tint/debug_test.cc",
|
"../../src/tint/debug_test.cc",
|
||||||
"../../src/tint/demangler_test.cc",
|
"../../src/tint/demangler_test.cc",
|
||||||
|
"../../src/tint/number_test.cc",
|
||||||
"../../src/tint/program_builder_test.cc",
|
"../../src/tint/program_builder_test.cc",
|
||||||
"../../src/tint/program_test.cc",
|
"../../src/tint/program_test.cc",
|
||||||
"../../src/tint/scope_stack_test.cc",
|
"../../src/tint/scope_stack_test.cc",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user