blob: a7bb2fe569b0edfb4beb615222956fdf826b9938 [file] [log] [blame]
// 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 "src/tint/utils/compiler_macros.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.
constexpr double kHighestF32 = 0x1.fffffep+127;
// Next ULP up from kHighestF32 for a float64.
constexpr double kHighestF32NextULP = 0x1.fffffe0000001p+127;
// Smallest positive normal float32 value.
constexpr double kSmallestF32 = 0x1p-126;
// Highest subnormal value for a float32.
constexpr double kHighestF32Subnormal = 0x0.fffffep-126;
// Highest float16 value.
constexpr double kHighestF16 = 0x1.ffcp+15;
// Next ULP up from kHighestF16 for a float64.
constexpr double kHighestF16NextULP = 0x1.ffc0000000001p+15;
// Smallest positive normal float16 value.
constexpr double kSmallestF16 = 0x1p-14;
// Highest subnormal value for a float32.
constexpr double kHighestF16Subnormal = 0x0.ffcp-14;
constexpr double kLowestF32 = -kHighestF32;
constexpr double kLowestF32NextULP = -kHighestF32NextULP;
constexpr double kLowestF16 = -kHighestF16;
constexpr double kLowestF16NextULP = -kHighestF16NextULP;
// MSVC (only in release builds) can grumble about some of the inlined numerical overflow /
// underflow that's done in this file. We like to think we know what we're doing, so silence the
// warning.
TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
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, CheckedConvertSubnormals) {
EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32Subnormal)), f32(kHighestF32Subnormal));
EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16Subnormal)), f16(kHighestF16Subnormal));
EXPECT_EQ(CheckedConvert<f32>(AFloat(-kHighestF32Subnormal)), f32(-kHighestF32Subnormal));
EXPECT_EQ(CheckedConvert<f16>(AFloat(-kHighestF16Subnormal)), f16(-kHighestF16Subnormal));
}
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)));
}
TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW);
} // namespace
} // namespace tint