Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 1 | // Copyright 2022 The Tint Authors. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include <cmath> |
Ben Clayton | 61537d3 | 2022-05-31 13:14:29 +0000 | [diff] [blame] | 16 | #include <tuple> |
| 17 | #include <vector> |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 18 | |
| 19 | #include "src/tint/program_builder.h" |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 20 | #include "src/tint/utils/compiler_macros.h" |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 21 | |
| 22 | #include "gtest/gtest.h" |
| 23 | |
| 24 | using namespace tint::number_suffixes; // NOLINT |
| 25 | |
| 26 | namespace tint { |
| 27 | namespace { |
| 28 | |
| 29 | constexpr int64_t kHighestI32 = static_cast<int64_t>(std::numeric_limits<int32_t>::max()); |
| 30 | constexpr int64_t kHighestU32 = static_cast<int64_t>(std::numeric_limits<uint32_t>::max()); |
| 31 | constexpr int64_t kLowestI32 = static_cast<int64_t>(std::numeric_limits<int32_t>::min()); |
| 32 | constexpr int64_t kLowestU32 = static_cast<int64_t>(std::numeric_limits<uint32_t>::min()); |
| 33 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 34 | // Highest float32 value. |
| 35 | constexpr double kHighestF32 = 0x1.fffffep+127; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 36 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 37 | // Next ULP up from kHighestF32 for a float64. |
| 38 | constexpr double kHighestF32NextULP = 0x1.fffffe0000001p+127; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 39 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 40 | // Smallest positive normal float32 value. |
| 41 | constexpr double kSmallestF32 = 0x1p-126; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 42 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 43 | // Highest subnormal value for a float32. |
| 44 | constexpr double kHighestF32Subnormal = 0x0.fffffep-126; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 45 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 46 | // Highest float16 value. |
| 47 | constexpr double kHighestF16 = 0x1.ffcp+15; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 48 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 49 | // Next ULP up from kHighestF16 for a float64. |
| 50 | constexpr double kHighestF16NextULP = 0x1.ffc0000000001p+15; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 51 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 52 | // Smallest positive normal float16 value. |
| 53 | constexpr double kSmallestF16 = 0x1p-14; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 54 | |
Zhaoming Jiang | 0fb4e2c | 2022-06-10 18:18:35 +0000 | [diff] [blame] | 55 | // Highest subnormal value for a float16. |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 56 | constexpr double kHighestF16Subnormal = 0x0.ffcp-14; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 57 | |
| 58 | constexpr double kLowestF32 = -kHighestF32; |
| 59 | constexpr double kLowestF32NextULP = -kHighestF32NextULP; |
| 60 | constexpr double kLowestF16 = -kHighestF16; |
| 61 | constexpr double kLowestF16NextULP = -kHighestF16NextULP; |
| 62 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 63 | // MSVC (only in release builds) can grumble about some of the inlined numerical overflow / |
| 64 | // underflow that's done in this file. We like to think we know what we're doing, so silence the |
| 65 | // warning. |
| 66 | TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW); |
| 67 | |
Ben Clayton | cf52af7 | 2022-06-29 17:09:11 +0000 | [diff] [blame] | 68 | TEST(NumberTest, Equality) { |
| 69 | EXPECT_TRUE(0_a == 0_a); |
| 70 | EXPECT_TRUE(10_a == 10_a); |
| 71 | EXPECT_TRUE(-10_a == -10_a); |
| 72 | |
| 73 | EXPECT_TRUE(0_i == 0_i); |
| 74 | EXPECT_TRUE(10_i == 10_i); |
| 75 | EXPECT_TRUE(-10_i == -10_i); |
| 76 | |
| 77 | EXPECT_TRUE(0_u == 0_u); |
| 78 | EXPECT_TRUE(10_u == 10_u); |
| 79 | |
| 80 | EXPECT_TRUE(0._a == 0._a); |
| 81 | EXPECT_TRUE(-0._a == -0._a); |
| 82 | EXPECT_TRUE(10._a == 10._a); |
| 83 | EXPECT_TRUE(-10._a == -10._a); |
| 84 | |
| 85 | EXPECT_TRUE(0_f == 0_f); |
| 86 | EXPECT_TRUE(-0_f == -0_f); |
| 87 | EXPECT_TRUE(10_f == 10_f); |
| 88 | EXPECT_TRUE(-10_f == -10_f); |
| 89 | |
| 90 | EXPECT_TRUE(0_h == 0_h); |
| 91 | EXPECT_TRUE(-0_h == -0_h); |
| 92 | EXPECT_TRUE(10_h == 10_h); |
| 93 | EXPECT_TRUE(-10_h == -10_h); |
| 94 | } |
| 95 | |
| 96 | TEST(NumberTest, Inequality) { |
| 97 | EXPECT_TRUE(0_a != 1_a); |
| 98 | EXPECT_TRUE(10_a != 11_a); |
| 99 | EXPECT_TRUE(11_a != 10_a); |
| 100 | EXPECT_TRUE(-10_a != -11_a); |
| 101 | EXPECT_TRUE(-11_a != -10_a); |
| 102 | |
| 103 | EXPECT_TRUE(0_i != 1_i); |
| 104 | EXPECT_TRUE(1_i != 0_i); |
| 105 | EXPECT_TRUE(10_i != 11_i); |
| 106 | EXPECT_TRUE(11_i != 10_i); |
| 107 | EXPECT_TRUE(-10_i != -11_i); |
| 108 | EXPECT_TRUE(-11_i != -10_i); |
| 109 | |
| 110 | EXPECT_TRUE(0_u != 1_u); |
| 111 | EXPECT_TRUE(1_u != 0_u); |
| 112 | EXPECT_TRUE(10_u != 11_u); |
| 113 | EXPECT_TRUE(11_u != 10_u); |
| 114 | |
| 115 | EXPECT_TRUE(0._a != -0._a); |
| 116 | EXPECT_TRUE(-0._a != 0._a); |
| 117 | EXPECT_TRUE(10._a != 11._a); |
| 118 | EXPECT_TRUE(11._a != 10._a); |
| 119 | EXPECT_TRUE(-10._a != -11._a); |
| 120 | EXPECT_TRUE(-11._a != -10._a); |
| 121 | |
| 122 | EXPECT_TRUE(0_f != -0_f); |
| 123 | EXPECT_TRUE(-0_f != 0_f); |
| 124 | EXPECT_TRUE(-0_f != -1_f); |
| 125 | EXPECT_TRUE(-1_f != -0_f); |
| 126 | EXPECT_TRUE(10_f != -10_f); |
| 127 | EXPECT_TRUE(-10_f != 10_f); |
| 128 | EXPECT_TRUE(10_f != 11_f); |
| 129 | EXPECT_TRUE(-10_f != -11_f); |
| 130 | |
| 131 | EXPECT_TRUE(0_h != -0_h); |
| 132 | EXPECT_TRUE(-0_h != 0_h); |
| 133 | EXPECT_TRUE(-0_h != -1_h); |
| 134 | EXPECT_TRUE(-1_h != -0_h); |
| 135 | EXPECT_TRUE(10_h != -10_h); |
| 136 | EXPECT_TRUE(-10_h != 10_h); |
| 137 | EXPECT_TRUE(10_h != 11_h); |
| 138 | EXPECT_TRUE(-10_h != -11_h); |
| 139 | } |
| 140 | |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 141 | TEST(NumberTest, CheckedConvertIdentity) { |
| 142 | EXPECT_EQ(CheckedConvert<AInt>(0_a), 0_a); |
| 143 | EXPECT_EQ(CheckedConvert<AFloat>(0_a), 0.0_a); |
| 144 | EXPECT_EQ(CheckedConvert<i32>(0_i), 0_i); |
| 145 | EXPECT_EQ(CheckedConvert<u32>(0_u), 0_u); |
| 146 | EXPECT_EQ(CheckedConvert<f32>(0_f), 0_f); |
| 147 | EXPECT_EQ(CheckedConvert<f16>(0_h), 0_h); |
| 148 | |
| 149 | EXPECT_EQ(CheckedConvert<AInt>(1_a), 1_a); |
| 150 | EXPECT_EQ(CheckedConvert<AFloat>(1_a), 1.0_a); |
| 151 | EXPECT_EQ(CheckedConvert<i32>(1_i), 1_i); |
| 152 | EXPECT_EQ(CheckedConvert<u32>(1_u), 1_u); |
| 153 | EXPECT_EQ(CheckedConvert<f32>(1_f), 1_f); |
| 154 | EXPECT_EQ(CheckedConvert<f16>(1_h), 1_h); |
| 155 | } |
| 156 | |
| 157 | TEST(NumberTest, CheckedConvertLargestValue) { |
| 158 | EXPECT_EQ(CheckedConvert<i32>(AInt(kHighestI32)), i32(kHighestI32)); |
| 159 | EXPECT_EQ(CheckedConvert<u32>(AInt(kHighestU32)), u32(kHighestU32)); |
Ben Clayton | 2b4df78 | 2022-06-29 11:31:41 +0000 | [diff] [blame] | 160 | EXPECT_EQ(CheckedConvert<u32>(i32(kHighestI32)), u32(kHighestI32)); |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 161 | EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32)), f32(kHighestF32)); |
| 162 | EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16)), f16(kHighestF16)); |
| 163 | } |
| 164 | |
| 165 | TEST(NumberTest, CheckedConvertLowestValue) { |
| 166 | EXPECT_EQ(CheckedConvert<i32>(AInt(kLowestI32)), i32(kLowestI32)); |
| 167 | EXPECT_EQ(CheckedConvert<u32>(AInt(kLowestU32)), u32(kLowestU32)); |
| 168 | EXPECT_EQ(CheckedConvert<f32>(AFloat(kLowestF32)), f32(kLowestF32)); |
| 169 | EXPECT_EQ(CheckedConvert<f16>(AFloat(kLowestF16)), f16(kLowestF16)); |
| 170 | } |
| 171 | |
| 172 | TEST(NumberTest, CheckedConvertSmallestValue) { |
| 173 | EXPECT_EQ(CheckedConvert<i32>(AInt(0)), i32(0)); |
| 174 | EXPECT_EQ(CheckedConvert<u32>(AInt(0)), u32(0)); |
| 175 | EXPECT_EQ(CheckedConvert<f32>(AFloat(kSmallestF32)), f32(kSmallestF32)); |
| 176 | EXPECT_EQ(CheckedConvert<f16>(AFloat(kSmallestF16)), f16(kSmallestF16)); |
| 177 | } |
| 178 | |
| 179 | TEST(NumberTest, CheckedConvertExceedsPositiveLimit) { |
| 180 | EXPECT_EQ(CheckedConvert<i32>(AInt(kHighestI32 + 1)), ConversionFailure::kExceedsPositiveLimit); |
| 181 | EXPECT_EQ(CheckedConvert<u32>(AInt(kHighestU32 + 1)), ConversionFailure::kExceedsPositiveLimit); |
Ben Clayton | 2b4df78 | 2022-06-29 11:31:41 +0000 | [diff] [blame] | 182 | EXPECT_EQ(CheckedConvert<i32>(u32(kHighestU32)), ConversionFailure::kExceedsPositiveLimit); |
| 183 | EXPECT_EQ(CheckedConvert<i32>(u32(0x80000000)), ConversionFailure::kExceedsPositiveLimit); |
| 184 | EXPECT_EQ(CheckedConvert<u32>(f32(f32::kHighest)), ConversionFailure::kExceedsPositiveLimit); |
| 185 | EXPECT_EQ(CheckedConvert<i32>(f32(f32::kHighest)), ConversionFailure::kExceedsPositiveLimit); |
| 186 | EXPECT_EQ(CheckedConvert<u32>(AFloat(AFloat::kHighest)), |
| 187 | ConversionFailure::kExceedsPositiveLimit); |
| 188 | EXPECT_EQ(CheckedConvert<i32>(AFloat(AFloat::kHighest)), |
| 189 | ConversionFailure::kExceedsPositiveLimit); |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 190 | EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32NextULP)), |
| 191 | ConversionFailure::kExceedsPositiveLimit); |
| 192 | EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16NextULP)), |
| 193 | ConversionFailure::kExceedsPositiveLimit); |
| 194 | } |
| 195 | |
| 196 | TEST(NumberTest, CheckedConvertExceedsNegativeLimit) { |
| 197 | EXPECT_EQ(CheckedConvert<i32>(AInt(kLowestI32 - 1)), ConversionFailure::kExceedsNegativeLimit); |
| 198 | EXPECT_EQ(CheckedConvert<u32>(AInt(kLowestU32 - 1)), ConversionFailure::kExceedsNegativeLimit); |
Ben Clayton | 2b4df78 | 2022-06-29 11:31:41 +0000 | [diff] [blame] | 199 | EXPECT_EQ(CheckedConvert<u32>(i32(-1)), ConversionFailure::kExceedsNegativeLimit); |
| 200 | EXPECT_EQ(CheckedConvert<u32>(i32(kLowestI32)), ConversionFailure::kExceedsNegativeLimit); |
| 201 | EXPECT_EQ(CheckedConvert<u32>(f32(f32::kLowest)), ConversionFailure::kExceedsNegativeLimit); |
| 202 | EXPECT_EQ(CheckedConvert<i32>(f32(f32::kLowest)), ConversionFailure::kExceedsNegativeLimit); |
| 203 | EXPECT_EQ(CheckedConvert<u32>(AFloat(AFloat::kLowest)), |
| 204 | ConversionFailure::kExceedsNegativeLimit); |
| 205 | EXPECT_EQ(CheckedConvert<i32>(AFloat(AFloat::kLowest)), |
| 206 | ConversionFailure::kExceedsNegativeLimit); |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 207 | EXPECT_EQ(CheckedConvert<f32>(AFloat(kLowestF32NextULP)), |
| 208 | ConversionFailure::kExceedsNegativeLimit); |
| 209 | EXPECT_EQ(CheckedConvert<f16>(AFloat(kLowestF16NextULP)), |
| 210 | ConversionFailure::kExceedsNegativeLimit); |
| 211 | } |
| 212 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 213 | TEST(NumberTest, CheckedConvertSubnormals) { |
| 214 | EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32Subnormal)), f32(kHighestF32Subnormal)); |
| 215 | EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16Subnormal)), f16(kHighestF16Subnormal)); |
| 216 | EXPECT_EQ(CheckedConvert<f32>(AFloat(-kHighestF32Subnormal)), f32(-kHighestF32Subnormal)); |
| 217 | EXPECT_EQ(CheckedConvert<f16>(AFloat(-kHighestF16Subnormal)), f16(-kHighestF16Subnormal)); |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 218 | } |
| 219 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 220 | // Test cases for f16 subnormal quantization and BitsRepresentation. |
| 221 | // The ULP is based on float rather than double or f16, since F16::Quantize and |
| 222 | // F16::BitsRepresentation take float as input. |
| 223 | constexpr float lowestPositiveNormalF16 = 0x1p-14; |
| 224 | constexpr float lowestPositiveNormalF16PlusULP = 0x1.000002p-14; |
| 225 | constexpr float lowestPositiveNormalF16MinusULP = 0x1.fffffep-15; |
| 226 | constexpr float highestPositiveSubnormalF16 = 0x0.ffcp-14; |
| 227 | constexpr float highestPositiveSubnormalF16PlusULP = 0x1.ff8002p-15; |
| 228 | constexpr float highestPositiveSubnormalF16MinusULP = 0x1.ff7ffep-15; |
| 229 | constexpr float lowestPositiveSubnormalF16 = 0x1.p-24; |
| 230 | constexpr float lowestPositiveSubnormalF16PlusULP = 0x1.000002p-24; |
| 231 | constexpr float lowestPositiveSubnormalF16MinusULP = 0x1.fffffep-25; |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 232 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 233 | constexpr uint16_t lowestPositiveNormalF16Bits = 0x0400u; |
| 234 | constexpr uint16_t highestPositiveSubnormalF16Bits = 0x03ffu; |
| 235 | constexpr uint16_t lowestPositiveSubnormalF16Bits = 0x0001u; |
Zhaoming Jiang | 0fb4e2c | 2022-06-10 18:18:35 +0000 | [diff] [blame] | 236 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 237 | constexpr float highestNegativeNormalF16 = -lowestPositiveNormalF16; |
| 238 | constexpr float highestNegativeNormalF16PlusULP = -lowestPositiveNormalF16MinusULP; |
| 239 | constexpr float highestNegativeNormalF16MinusULP = -lowestPositiveNormalF16PlusULP; |
| 240 | constexpr float lowestNegativeSubnormalF16 = -highestPositiveSubnormalF16; |
| 241 | constexpr float lowestNegativeSubnormalF16PlusULP = -highestPositiveSubnormalF16MinusULP; |
| 242 | constexpr float lowestNegativeSubnormalF16MinusULP = -highestPositiveSubnormalF16PlusULP; |
| 243 | constexpr float highestNegativeSubnormalF16 = -lowestPositiveSubnormalF16; |
| 244 | constexpr float highestNegativeSubnormalF16PlusULP = -lowestPositiveSubnormalF16MinusULP; |
| 245 | constexpr float highestNegativeSubnormalF16MinusULP = -lowestPositiveSubnormalF16PlusULP; |
Zhaoming Jiang | 0fb4e2c | 2022-06-10 18:18:35 +0000 | [diff] [blame] | 246 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 247 | constexpr uint16_t highestNegativeNormalF16Bits = 0x8400u; |
| 248 | constexpr uint16_t lowestNegativeSubnormalF16Bits = 0x83ffu; |
| 249 | constexpr uint16_t highestNegativeSubnormalF16Bits = 0x8001u; |
Zhaoming Jiang | 0fb4e2c | 2022-06-10 18:18:35 +0000 | [diff] [blame] | 250 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 251 | constexpr float f32_nan = std::numeric_limits<float>::quiet_NaN(); |
| 252 | constexpr float f32_inf = std::numeric_limits<float>::infinity(); |
Zhaoming Jiang | 0fb4e2c | 2022-06-10 18:18:35 +0000 | [diff] [blame] | 253 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 254 | struct F16TestCase { |
| 255 | float input_value; |
| 256 | float quantized_value; |
| 257 | uint16_t f16_bit_pattern; |
| 258 | }; |
| 259 | |
| 260 | using NumberF16Test = testing::TestWithParam<F16TestCase>; |
| 261 | |
| 262 | TEST_P(NumberF16Test, QuantizeF16) { |
| 263 | float input_value = GetParam().input_value; |
| 264 | float quantized_value = GetParam().quantized_value; |
| 265 | |
| 266 | std::stringstream ss; |
| 267 | ss << "input value = " << input_value << ", expected quantized value = " << quantized_value; |
| 268 | SCOPED_TRACE(ss.str()); |
| 269 | |
| 270 | if (std::isnan(quantized_value)) { |
| 271 | EXPECT_TRUE(std::isnan(f16(input_value))); |
| 272 | } else { |
| 273 | EXPECT_EQ(f16(input_value), quantized_value); |
| 274 | } |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 275 | } |
| 276 | |
Zhaoming Jiang | 2c7440a | 2022-07-07 03:29:11 +0000 | [diff] [blame] | 277 | TEST_P(NumberF16Test, BitsRepresentation) { |
| 278 | float input_value = GetParam().input_value; |
| 279 | uint16_t representation = GetParam().f16_bit_pattern; |
| 280 | |
| 281 | std::stringstream ss; |
| 282 | ss << "input value = " << input_value |
| 283 | << ", expected binary16 bits representation = " << std::hex << std::showbase |
| 284 | << representation; |
| 285 | SCOPED_TRACE(ss.str()); |
| 286 | |
| 287 | EXPECT_EQ(f16(input_value).BitsRepresentation(), representation); |
| 288 | } |
| 289 | |
| 290 | INSTANTIATE_TEST_SUITE_P( |
| 291 | NumberF16Test, |
| 292 | NumberF16Test, |
| 293 | testing::ValuesIn(std::vector<F16TestCase>{ |
| 294 | // NaN, Inf |
| 295 | {f32_inf, f32_inf, 0x7c00u}, |
| 296 | {-f32_inf, -f32_inf, 0xfc00u}, |
| 297 | {f32_nan, f32_nan, 0x7e00u}, |
| 298 | {-f32_nan, -f32_nan, 0x7e00u}, |
| 299 | // +/- zero |
| 300 | {+0.0f, 0.0f, 0x0000u}, |
| 301 | {-0.0f, -0.0f, 0x8000u}, |
| 302 | // Value in normal f16 range |
| 303 | {1.0f, 1.0f, 0x3c00u}, |
| 304 | {-1.0f, -1.0f, 0xbc00u}, |
| 305 | // 0.00006106496 quantized to 0.000061035156 = 0x1p-14 |
| 306 | {0.00006106496f, 0.000061035156f, 0x0400u}, |
| 307 | {-0.00006106496f, -0.000061035156f, 0x8400u}, |
| 308 | // 1.0004883 quantized to 1.0 = 0x1p0 |
| 309 | {1.0004883f, 1.0f, 0x3c00u}, |
| 310 | {-1.0004883f, -1.0f, 0xbc00u}, |
| 311 | // 8196.0 quantized to 8192.0 = 0x1p13 |
| 312 | {8196.0f, 8192.f, 0x7000u}, |
| 313 | {-8196.0f, -8192.f, 0xf000u}, |
| 314 | // Value in subnormal f16 range |
| 315 | {0x0.034p-14f, 0x0.034p-14f, 0x000du}, |
| 316 | {-0x0.034p-14f, -0x0.034p-14f, 0x800du}, |
| 317 | {0x0.068p-14f, 0x0.068p-14f, 0x001au}, |
| 318 | {-0x0.068p-14f, -0x0.068p-14f, 0x801au}, |
| 319 | // 0x0.06b7p-14 quantized to 0x0.068p-14 |
| 320 | {0x0.06b7p-14f, 0x0.068p-14f, 0x001au}, |
| 321 | {-0x0.06b7p-14f, -0x0.068p-14, 0x801au}, |
| 322 | // Value out of f16 range |
| 323 | {65504.003f, f32_inf, 0x7c00u}, |
| 324 | {-65504.003f, -f32_inf, 0xfc00u}, |
| 325 | {0x1.234p56f, f32_inf, 0x7c00u}, |
| 326 | {-0x4.321p65f, -f32_inf, 0xfc00u}, |
| 327 | |
| 328 | // Test for subnormal quantization. |
| 329 | // Value larger than or equal to lowest positive normal f16 will be quantized to normal f16. |
| 330 | {lowestPositiveNormalF16PlusULP, lowestPositiveNormalF16, lowestPositiveNormalF16Bits}, |
| 331 | {lowestPositiveNormalF16, lowestPositiveNormalF16, lowestPositiveNormalF16Bits}, |
| 332 | // Positive value smaller than lowest positive normal f16 but not smaller than lowest |
| 333 | // positive |
| 334 | // subnormal f16 will be quantized to subnormal f16 or zero. |
| 335 | {lowestPositiveNormalF16MinusULP, highestPositiveSubnormalF16, |
| 336 | highestPositiveSubnormalF16Bits}, |
| 337 | {highestPositiveSubnormalF16PlusULP, highestPositiveSubnormalF16, |
| 338 | highestPositiveSubnormalF16Bits}, |
| 339 | {highestPositiveSubnormalF16, highestPositiveSubnormalF16, highestPositiveSubnormalF16Bits}, |
| 340 | {highestPositiveSubnormalF16MinusULP, 0x0.ff8p-14, 0x03feu}, |
| 341 | {lowestPositiveSubnormalF16PlusULP, lowestPositiveSubnormalF16, |
| 342 | lowestPositiveSubnormalF16Bits}, |
| 343 | {lowestPositiveSubnormalF16, lowestPositiveSubnormalF16, lowestPositiveSubnormalF16Bits}, |
| 344 | // Positive value smaller than lowest positive subnormal f16 will be quantized to zero. |
| 345 | {lowestPositiveSubnormalF16MinusULP, 0.0, 0x0000u}, |
| 346 | // Test the mantissa discarding, the least significant mantissa bit is 0x1p-24 = |
| 347 | // 0x0.004p-14. |
| 348 | {0x0.064p-14f, 0x0.064p-14, 0x0019u}, |
| 349 | {0x0.067fecp-14f, 0x0.064p-14, 0x0019u}, |
| 350 | {0x0.063ffep-14f, 0x0.060p-14, 0x0018u}, |
| 351 | {0x0.008p-14f, 0x0.008p-14, 0x0002u}, |
| 352 | {0x0.00bffep-14f, 0x0.008p-14, 0x0002u}, |
| 353 | {0x0.007ffep-14f, 0x0.004p-14, 0x0001u}, |
| 354 | |
| 355 | // Vice versa for negative cases. |
| 356 | {highestNegativeNormalF16MinusULP, highestNegativeNormalF16, highestNegativeNormalF16Bits}, |
| 357 | {highestNegativeNormalF16, highestNegativeNormalF16, highestNegativeNormalF16Bits}, |
| 358 | {highestNegativeNormalF16PlusULP, lowestNegativeSubnormalF16, |
| 359 | lowestNegativeSubnormalF16Bits}, |
| 360 | {lowestNegativeSubnormalF16MinusULP, lowestNegativeSubnormalF16, |
| 361 | lowestNegativeSubnormalF16Bits}, |
| 362 | {lowestNegativeSubnormalF16, lowestNegativeSubnormalF16, lowestNegativeSubnormalF16Bits}, |
| 363 | {lowestNegativeSubnormalF16PlusULP, -0x0.ff8p-14, 0x83feu}, |
| 364 | {highestNegativeSubnormalF16MinusULP, highestNegativeSubnormalF16, |
| 365 | highestNegativeSubnormalF16Bits}, |
| 366 | {highestNegativeSubnormalF16, highestNegativeSubnormalF16, highestNegativeSubnormalF16Bits}, |
| 367 | {highestNegativeSubnormalF16PlusULP, -0.0, 0x8000u}, |
| 368 | // Test the mantissa discarding. |
| 369 | {-0x0.064p-14f, -0x0.064p-14, 0x8019u}, |
| 370 | {-0x0.067fecp-14f, -0x0.064p-14, 0x8019u}, |
| 371 | {-0x0.063ffep-14f, -0x0.060p-14, 0x8018u}, |
| 372 | {-0x0.008p-14f, -0x0.008p-14, 0x8002u}, |
| 373 | {-0x0.00bffep-14f, -0x0.008p-14, 0x8002u}, |
| 374 | {-0x0.007ffep-14f, -0x0.004p-14, 0x8001u}, |
| 375 | ///////////////////////////////////// |
| 376 | })); |
| 377 | |
Ben Clayton | 61537d3 | 2022-05-31 13:14:29 +0000 | [diff] [blame] | 378 | using BinaryCheckedCase = std::tuple<std::optional<AInt>, AInt, AInt>; |
| 379 | |
| 380 | #undef OVERFLOW // corecrt_math.h :( |
| 381 | #define OVERFLOW \ |
| 382 | {} |
| 383 | |
| 384 | using CheckedAddTest = testing::TestWithParam<BinaryCheckedCase>; |
| 385 | TEST_P(CheckedAddTest, Test) { |
| 386 | auto expect = std::get<0>(GetParam()); |
| 387 | auto a = std::get<1>(GetParam()); |
| 388 | auto b = std::get<2>(GetParam()); |
| 389 | EXPECT_EQ(CheckedAdd(a, b), expect) << std::hex << "0x" << a << " * 0x" << b; |
| 390 | EXPECT_EQ(CheckedAdd(b, a), expect) << std::hex << "0x" << a << " * 0x" << b; |
| 391 | } |
| 392 | INSTANTIATE_TEST_SUITE_P( |
| 393 | CheckedAddTest, |
| 394 | CheckedAddTest, |
| 395 | testing::ValuesIn(std::vector<BinaryCheckedCase>{ |
| 396 | {AInt(0), AInt(0), AInt(0)}, |
| 397 | {AInt(1), AInt(1), AInt(0)}, |
| 398 | {AInt(2), AInt(1), AInt(1)}, |
| 399 | {AInt(0), AInt(-1), AInt(1)}, |
| 400 | {AInt(3), AInt(2), AInt(1)}, |
| 401 | {AInt(-1), AInt(-2), AInt(1)}, |
| 402 | {AInt(0x300), AInt(0x100), AInt(0x200)}, |
| 403 | {AInt(0x100), AInt(-0x100), AInt(0x200)}, |
| 404 | {AInt(AInt::kHighest), AInt(1), AInt(AInt::kHighest - 1)}, |
| 405 | {AInt(AInt::kLowest), AInt(-1), AInt(AInt::kLowest + 1)}, |
| 406 | {AInt(AInt::kHighest), AInt(0x7fffffff00000000ll), AInt(0x00000000ffffffffll)}, |
| 407 | {AInt(AInt::kHighest), AInt(AInt::kHighest), AInt(0)}, |
| 408 | {AInt(AInt::kLowest), AInt(AInt::kLowest), AInt(0)}, |
| 409 | {OVERFLOW, AInt(1), AInt(AInt::kHighest)}, |
| 410 | {OVERFLOW, AInt(-1), AInt(AInt::kLowest)}, |
| 411 | {OVERFLOW, AInt(2), AInt(AInt::kHighest)}, |
| 412 | {OVERFLOW, AInt(-2), AInt(AInt::kLowest)}, |
| 413 | {OVERFLOW, AInt(10000), AInt(AInt::kHighest)}, |
| 414 | {OVERFLOW, AInt(-10000), AInt(AInt::kLowest)}, |
| 415 | {OVERFLOW, AInt(AInt::kHighest), AInt(AInt::kHighest)}, |
| 416 | {OVERFLOW, AInt(AInt::kLowest), AInt(AInt::kLowest)}, |
| 417 | //////////////////////////////////////////////////////////////////////// |
| 418 | })); |
| 419 | |
| 420 | using CheckedMulTest = testing::TestWithParam<BinaryCheckedCase>; |
| 421 | TEST_P(CheckedMulTest, Test) { |
| 422 | auto expect = std::get<0>(GetParam()); |
| 423 | auto a = std::get<1>(GetParam()); |
| 424 | auto b = std::get<2>(GetParam()); |
| 425 | EXPECT_EQ(CheckedMul(a, b), expect) << std::hex << "0x" << a << " * 0x" << b; |
| 426 | EXPECT_EQ(CheckedMul(b, a), expect) << std::hex << "0x" << a << " * 0x" << b; |
| 427 | } |
| 428 | INSTANTIATE_TEST_SUITE_P( |
| 429 | CheckedMulTest, |
| 430 | CheckedMulTest, |
| 431 | testing::ValuesIn(std::vector<BinaryCheckedCase>{ |
| 432 | {AInt(0), AInt(0), AInt(0)}, |
| 433 | {AInt(0), AInt(1), AInt(0)}, |
| 434 | {AInt(1), AInt(1), AInt(1)}, |
| 435 | {AInt(-1), AInt(-1), AInt(1)}, |
| 436 | {AInt(2), AInt(2), AInt(1)}, |
| 437 | {AInt(-2), AInt(-2), AInt(1)}, |
| 438 | {AInt(0x20000), AInt(0x100), AInt(0x200)}, |
| 439 | {AInt(-0x20000), AInt(-0x100), AInt(0x200)}, |
| 440 | {AInt(0x4000000000000000ll), AInt(0x80000000ll), AInt(0x80000000ll)}, |
| 441 | {AInt(0x4000000000000000ll), AInt(-0x80000000ll), AInt(-0x80000000ll)}, |
| 442 | {AInt(0x1000000000000000ll), AInt(0x40000000ll), AInt(0x40000000ll)}, |
| 443 | {AInt(-0x1000000000000000ll), AInt(-0x40000000ll), AInt(0x40000000ll)}, |
| 444 | {AInt(0x100000000000000ll), AInt(0x1000000), AInt(0x100000000ll)}, |
| 445 | {AInt(0x2000000000000000ll), AInt(0x1000000000000000ll), AInt(2)}, |
| 446 | {AInt(-0x2000000000000000ll), AInt(0x1000000000000000ll), AInt(-2)}, |
| 447 | {AInt(-0x2000000000000000ll), AInt(-0x1000000000000000ll), AInt(2)}, |
| 448 | {AInt(-0x2000000000000000ll), AInt(0x1000000000000000ll), AInt(-2)}, |
| 449 | {AInt(0x4000000000000000ll), AInt(0x1000000000000000ll), AInt(4)}, |
| 450 | {AInt(-0x4000000000000000ll), AInt(0x1000000000000000ll), AInt(-4)}, |
| 451 | {AInt(-0x4000000000000000ll), AInt(-0x1000000000000000ll), AInt(4)}, |
| 452 | {AInt(-0x4000000000000000ll), AInt(0x1000000000000000ll), AInt(-4)}, |
| 453 | {AInt(-0x8000000000000000ll), AInt(0x1000000000000000ll), AInt(-8)}, |
| 454 | {AInt(-0x8000000000000000ll), AInt(-0x1000000000000000ll), AInt(8)}, |
| 455 | {AInt(0), AInt(AInt::kHighest), AInt(0)}, |
| 456 | {AInt(0), AInt(AInt::kLowest), AInt(0)}, |
| 457 | {OVERFLOW, AInt(0x1000000000000000ll), AInt(8)}, |
| 458 | {OVERFLOW, AInt(-0x1000000000000000ll), AInt(-8)}, |
| 459 | {OVERFLOW, AInt(0x800000000000000ll), AInt(0x10)}, |
| 460 | {OVERFLOW, AInt(0x80000000ll), AInt(0x100000000ll)}, |
| 461 | {OVERFLOW, AInt(AInt::kHighest), AInt(AInt::kHighest)}, |
| 462 | {OVERFLOW, AInt(AInt::kHighest), AInt(AInt::kLowest)}, |
| 463 | //////////////////////////////////////////////////////////////////////// |
| 464 | })); |
| 465 | |
| 466 | using TernaryCheckedCase = std::tuple<std::optional<AInt>, AInt, AInt, AInt>; |
| 467 | |
| 468 | using CheckedMaddTest = testing::TestWithParam<TernaryCheckedCase>; |
| 469 | TEST_P(CheckedMaddTest, Test) { |
| 470 | auto expect = std::get<0>(GetParam()); |
| 471 | auto a = std::get<1>(GetParam()); |
| 472 | auto b = std::get<2>(GetParam()); |
| 473 | auto c = std::get<3>(GetParam()); |
| 474 | EXPECT_EQ(CheckedMadd(a, b, c), expect) |
| 475 | << std::hex << "0x" << a << " * 0x" << b << " + 0x" << c; |
| 476 | EXPECT_EQ(CheckedMadd(b, a, c), expect) |
| 477 | << std::hex << "0x" << a << " * 0x" << b << " + 0x" << c; |
| 478 | } |
| 479 | INSTANTIATE_TEST_SUITE_P( |
| 480 | CheckedMaddTest, |
| 481 | CheckedMaddTest, |
| 482 | testing::ValuesIn(std::vector<TernaryCheckedCase>{ |
| 483 | {AInt(0), AInt(0), AInt(0), AInt(0)}, |
| 484 | {AInt(0), AInt(1), AInt(0), AInt(0)}, |
| 485 | {AInt(1), AInt(1), AInt(1), AInt(0)}, |
| 486 | {AInt(2), AInt(1), AInt(1), AInt(1)}, |
| 487 | {AInt(0), AInt(1), AInt(-1), AInt(1)}, |
| 488 | {AInt(-1), AInt(1), AInt(-2), AInt(1)}, |
| 489 | {AInt(-1), AInt(-1), AInt(1), AInt(0)}, |
| 490 | {AInt(2), AInt(2), AInt(1), AInt(0)}, |
| 491 | {AInt(-2), AInt(-2), AInt(1), AInt(0)}, |
| 492 | {AInt(0), AInt(AInt::kHighest), AInt(0), AInt(0)}, |
| 493 | {AInt(0), AInt(AInt::kLowest), AInt(0), AInt(0)}, |
| 494 | {AInt(3), AInt(1), AInt(2), AInt(1)}, |
| 495 | {AInt(0x300), AInt(1), AInt(0x100), AInt(0x200)}, |
| 496 | {AInt(0x100), AInt(1), AInt(-0x100), AInt(0x200)}, |
| 497 | {AInt(0x20000), AInt(0x100), AInt(0x200), AInt(0)}, |
| 498 | {AInt(-0x20000), AInt(-0x100), AInt(0x200), AInt(0)}, |
| 499 | {AInt(0x4000000000000000ll), AInt(0x80000000ll), AInt(0x80000000ll), AInt(0)}, |
| 500 | {AInt(0x4000000000000000ll), AInt(-0x80000000ll), AInt(-0x80000000ll), AInt(0)}, |
| 501 | {AInt(0x1000000000000000ll), AInt(0x40000000ll), AInt(0x40000000ll), AInt(0)}, |
| 502 | {AInt(-0x1000000000000000ll), AInt(-0x40000000ll), AInt(0x40000000ll), AInt(0)}, |
| 503 | {AInt(0x100000000000000ll), AInt(0x1000000), AInt(0x100000000ll), AInt(0)}, |
| 504 | {AInt(0x2000000000000000ll), AInt(0x1000000000000000ll), AInt(2), AInt(0)}, |
| 505 | {AInt(-0x2000000000000000ll), AInt(0x1000000000000000ll), AInt(-2), AInt(0)}, |
| 506 | {AInt(-0x2000000000000000ll), AInt(-0x1000000000000000ll), AInt(2), AInt(0)}, |
| 507 | {AInt(-0x2000000000000000ll), AInt(0x1000000000000000ll), AInt(-2), AInt(0)}, |
| 508 | {AInt(0x4000000000000000ll), AInt(0x1000000000000000ll), AInt(4), AInt(0)}, |
| 509 | {AInt(-0x4000000000000000ll), AInt(0x1000000000000000ll), AInt(-4), AInt(0)}, |
| 510 | {AInt(-0x4000000000000000ll), AInt(-0x1000000000000000ll), AInt(4), AInt(0)}, |
| 511 | {AInt(-0x4000000000000000ll), AInt(0x1000000000000000ll), AInt(-4), AInt(0)}, |
| 512 | {AInt(-0x8000000000000000ll), AInt(0x1000000000000000ll), AInt(-8), AInt(0)}, |
| 513 | {AInt(-0x8000000000000000ll), AInt(-0x1000000000000000ll), AInt(8), AInt(0)}, |
| 514 | {AInt(AInt::kHighest), AInt(1), AInt(1), AInt(AInt::kHighest - 1)}, |
| 515 | {AInt(AInt::kLowest), AInt(1), AInt(-1), AInt(AInt::kLowest + 1)}, |
| 516 | {AInt(AInt::kHighest), AInt(1), AInt(0x7fffffff00000000ll), AInt(0x00000000ffffffffll)}, |
| 517 | {AInt(AInt::kHighest), AInt(1), AInt(AInt::kHighest), AInt(0)}, |
| 518 | {AInt(AInt::kLowest), AInt(1), AInt(AInt::kLowest), AInt(0)}, |
| 519 | {OVERFLOW, AInt(0x1000000000000000ll), AInt(8), AInt(0)}, |
| 520 | {OVERFLOW, AInt(-0x1000000000000000ll), AInt(-8), AInt(0)}, |
| 521 | {OVERFLOW, AInt(0x800000000000000ll), AInt(0x10), AInt(0)}, |
| 522 | {OVERFLOW, AInt(0x80000000ll), AInt(0x100000000ll), AInt(0)}, |
| 523 | {OVERFLOW, AInt(AInt::kHighest), AInt(AInt::kHighest), AInt(0)}, |
| 524 | {OVERFLOW, AInt(AInt::kHighest), AInt(AInt::kLowest), AInt(0)}, |
| 525 | {OVERFLOW, AInt(1), AInt(1), AInt(AInt::kHighest)}, |
| 526 | {OVERFLOW, AInt(1), AInt(-1), AInt(AInt::kLowest)}, |
| 527 | {OVERFLOW, AInt(1), AInt(2), AInt(AInt::kHighest)}, |
| 528 | {OVERFLOW, AInt(1), AInt(-2), AInt(AInt::kLowest)}, |
| 529 | {OVERFLOW, AInt(1), AInt(10000), AInt(AInt::kHighest)}, |
| 530 | {OVERFLOW, AInt(1), AInt(-10000), AInt(AInt::kLowest)}, |
| 531 | {OVERFLOW, AInt(1), AInt(AInt::kHighest), AInt(AInt::kHighest)}, |
| 532 | {OVERFLOW, AInt(1), AInt(AInt::kLowest), AInt(AInt::kLowest)}, |
| 533 | {OVERFLOW, AInt(1), AInt(AInt::kHighest), AInt(1)}, |
| 534 | {OVERFLOW, AInt(1), AInt(AInt::kLowest), AInt(-1)}, |
| 535 | })); |
| 536 | |
Ben Clayton | 572eaf2 | 2022-05-27 20:22:26 +0000 | [diff] [blame] | 537 | TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW); |
| 538 | |
Ben Clayton | c2eccfc | 2022-05-25 15:04:24 +0000 | [diff] [blame] | 539 | } // namespace |
| 540 | } // namespace tint |