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/scalar.cc",
|
||||
"inspector/scalar.h",
|
||||
"number.cc",
|
||||
"number.h",
|
||||
"program.cc",
|
||||
"program.h",
|
||||
|
|
|
@ -239,6 +239,7 @@ set(TINT_LIB_SRCS
|
|||
inspector/resource_binding.h
|
||||
inspector/scalar.cc
|
||||
inspector/scalar.h
|
||||
number.cc
|
||||
number.h
|
||||
program_builder.cc
|
||||
program_builder.h
|
||||
|
@ -748,21 +749,23 @@ if(TINT_BUILD_TESTS)
|
|||
diagnostic/diagnostic_test.cc
|
||||
diagnostic/formatter_test.cc
|
||||
diagnostic/printer_test.cc
|
||||
number_test.cc
|
||||
program_builder_test.cc
|
||||
program_test.cc
|
||||
resolver/array_accessor_test.cc
|
||||
resolver/assignment_validation_test.cc
|
||||
resolver/atomics_test.cc
|
||||
resolver/atomics_validation_test.cc
|
||||
resolver/attribute_validation_test.cc
|
||||
resolver/bitcast_validation_test.cc
|
||||
resolver/builtins_validation_test.cc
|
||||
resolver/builtin_test.cc
|
||||
resolver/builtin_validation_test.cc
|
||||
resolver/builtins_validation_test.cc
|
||||
resolver/call_test.cc
|
||||
resolver/call_validation_test.cc
|
||||
resolver/compound_assignment_validation_test.cc
|
||||
resolver/compound_statement_test.cc
|
||||
resolver/control_block_validation_test.cc
|
||||
resolver/attribute_validation_test.cc
|
||||
resolver/dependency_graph_test.cc
|
||||
resolver/entry_point_validation_test.cc
|
||||
resolver/function_validation_test.cc
|
||||
|
@ -815,8 +818,8 @@ if(TINT_BUILD_TESTS)
|
|||
sem/sem_struct_test.cc
|
||||
sem/storage_texture_test.cc
|
||||
sem/texture_test.cc
|
||||
sem/type_test.cc
|
||||
sem/type_manager_test.cc
|
||||
sem/type_test.cc
|
||||
sem/u32_test.cc
|
||||
sem/vector_test.cc
|
||||
source_test.cc
|
||||
|
|
|
@ -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 <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 {
|
||||
/// 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 {
|
||||
/// 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 {
|
||||
|
||||
/// 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.
|
||||
template <typename T>
|
||||
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.
|
||||
Number() = default;
|
||||
|
||||
|
@ -59,41 +113,139 @@ struct Number {
|
|||
}
|
||||
|
||||
/// 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>
|
||||
bool operator==(Number<A> a, Number<B> b) {
|
||||
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>
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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>
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// and enforcing proper explicit casting.
|
||||
template <>
|
||||
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.
|
||||
Number() = default;
|
||||
|
||||
/// Constructor.
|
||||
/// @param v the value to initialize this Number to
|
||||
template <typename U>
|
||||
explicit Number(U v) : value(static_cast<float>(v)) {}
|
||||
explicit Number(U v) : value(Quantize(static_cast<type>(v))) {}
|
||||
|
||||
/// Constructor.
|
||||
/// @param v the value to initialize this Number to
|
||||
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
|
||||
/// @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
|
||||
/// @param v the new value
|
||||
/// @returns this Number so calls can be chained
|
||||
Number& operator=(float v) {
|
||||
value = v;
|
||||
Number& operator=(type v) {
|
||||
value = Quantize(v);
|
||||
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
|
||||
float value = {};
|
||||
type value = {};
|
||||
};
|
||||
|
||||
/// `AInt` is a type alias to `Number<int64_t>`.
|
||||
|
|
|
@ -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/debug_test.cc",
|
||||
"../../src/tint/demangler_test.cc",
|
||||
"../../src/tint/number_test.cc",
|
||||
"../../src/tint/program_builder_test.cc",
|
||||
"../../src/tint/program_test.cc",
|
||||
"../../src/tint/scope_stack_test.cc",
|
||||
|
|
Loading…
Reference in New Issue