openvino/src/core/tests/float8_e4m3.cpp

176 lines
7.2 KiB
C++

// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "openvino/core/type/float8_e4m3.hpp"
#include <gtest/gtest.h>
#include <climits>
#include "common_test_utils/float_util.hpp"
namespace ov {
namespace test {
template <class TContainer>
std::vector<std::tuple<int, typename TContainer::value_type>> enumerate(const TContainer& values) {
std::vector<std::tuple<int, typename TContainer::value_type>> enum_values;
int i = 0;
for (const auto& v : values) {
enum_values.emplace_back(i, v);
++i;
}
return enum_values;
}
TEST(F8E4M3Test, f32_inf) {
const auto f8 = ov::float8_e4m3(std::numeric_limits<float>::infinity());
EXPECT_EQ(f8.to_bits(), 0x7f);
}
TEST(F8E4M3Test, f32_minus_inf) {
const auto f8 = ov::float8_e4m3(-std::numeric_limits<float>::infinity());
// f8 is NaN as there is no infinity
EXPECT_EQ(f8.to_bits(), 0xff);
}
TEST(F8E4M3Test, f32_nan) {
const auto f8 = ov::float8_e4m3(std::numeric_limits<float>::quiet_NaN());
EXPECT_EQ(f8.to_bits(), 0x7f);
}
TEST(F8E4M3Test, f32_gt_zero_le_f8_half_lowest_subnormal) {
const auto f8 = ov::float8_e4m3(0.0009765625f);
EXPECT_EQ(f8.to_bits(), 0x00);
}
TEST(F8E4M3Test, f32_gt_zero_gt_f8_half_lowest_subnormal) {
const auto f8 = ov::float8_e4m3(0.00097656273283064365387f);
EXPECT_EQ(f8.to_bits(), 0x01);
}
TEST(F8E4M3Test, f32_normal_fractional_rounding) {
const auto f8 = ov::float8_e4m3(0.129f);
// Rounded to 0.140625f -> 0x21
EXPECT_EQ(f8.to_bits(), 0x20);
}
TEST(F8E4M3Test, f32_normal_negative_fractional_rounding) {
const auto f8 = ov::float8_e4m3(-0.281f);
// Rounded to -0.28125f -> 0x21
EXPECT_EQ(f8.to_bits(), 0xa9);
}
TEST(F8E4M3Test, f32_ge_f8_max_within_round_to_max) {
const auto f8 = ov::float8_e4m3(460.0f);
// Rounded to 448.0f -> 0x7e
EXPECT_EQ(f8.to_bits(), 0x7e);
}
TEST(F8E4M3Test, f32_ge_f8_max_not_within_round_to_max) {
const auto f8 = ov::float8_e4m3(560.0f);
// f8 has no such value (NaN)
EXPECT_EQ(f8.to_bits(), 0x7f);
}
TEST(F8E4M3Test, f32_le_f8_lowest_within_round_to_lowest) {
const auto f8 = ov::float8_e4m3(-460.0f);
// Rounded to -448.0f -> 0xfe
EXPECT_EQ(f8.to_bits(), 0xfe);
}
TEST(F8E4M3Test, f32_le_f8_lowest_not_within_round_to_lowest) {
const auto f8 = ov::float8_e4m3(-760.0f);
// f8 has no such value (NaN)
EXPECT_EQ(f8.to_bits(), 0xff);
}
TEST(F8E4M3Test, stream_operator) {
std::stringstream s;
s << ov::float8_e4m3(2.5f);
EXPECT_EQ(s.str(), "2.5");
}
TEST(F8E4M3Test, to_string) {
const auto f8 = ov::float8_e4m3::from_bits(0b00111010);
EXPECT_EQ(std::to_string(f8), "1.250000");
}
constexpr auto f32_qnan = std::numeric_limits<float>::quiet_NaN();
const auto exp_floats = std::vector<float>{
0.0f, 0.001953125f, 0.00390625f, 0.005859375f, 0.0078125f, 0.009765625f, 0.01171875f, 0.013671875f,
0.015625f, 0.017578125f, 0.01953125f, 0.021484375f, 0.0234375f, 0.025390625f, 0.02734375f, 0.029296875f,
0.03125f, 0.03515625f, 0.0390625f, 0.04296875f, 0.046875f, 0.05078125f, 0.0546875f, 0.05859375f,
0.0625f, 0.0703125f, 0.078125f, 0.0859375f, 0.09375f, 0.1015625f, 0.109375f, 0.1171875f,
0.125f, 0.140625f, 0.15625f, 0.171875f, 0.1875f, 0.203125f, 0.21875f, 0.234375f,
0.25f, 0.28125f, 0.3125f, 0.34375f, 0.375f, 0.40625f, 0.4375f, 0.46875f,
0.5f, 0.5625f, 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, 0.9375f,
1.0f, 1.125f, 1.25f, 1.375f, 1.5f, 1.625f, 1.75f, 1.875f,
2.0f, 2.25f, 2.5f, 2.75f, 3.0f, 3.25f, 3.5f, 3.75f,
4.0f, 4.5f, 5.0f, 5.5f, 6.0f, 6.5f, 7.0f, 7.5f,
8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f,
16.0f, 18.0f, 20.0f, 22.0f, 24.0f, 26.0f, 28.0f, 30.0f,
32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f,
64.0f, 72.0f, 80.0f, 88.0f, 96.0f, 104.0f, 112.0f, 120.0f,
128.0f, 144.0f, 160.0f, 176.0f, 192.0f, 208.0f, 224.0f, 240.0f,
256.0f, 288.0f, 320.0f, 352.0f, 384.0f, 416.0f, 448.0f, f32_qnan,
-0.0f, -0.001953125f, -0.00390625f, -0.005859375f, -0.0078125f, -0.009765625f, -0.01171875f, -0.013671875f,
-0.015625f, -0.017578125f, -0.01953125f, -0.021484375f, -0.0234375f, -0.025390625f, -0.02734375f, -0.029296875f,
-0.03125f, -0.03515625f, -0.0390625f, -0.04296875f, -0.046875f, -0.05078125f, -0.0546875f, -0.05859375f,
-0.0625f, -0.0703125f, -0.078125f, -0.0859375f, -0.09375f, -0.1015625f, -0.109375f, -0.1171875f,
-0.125f, -0.140625f, -0.15625f, -0.171875f, -0.1875f, -0.203125f, -0.21875f, -0.234375f,
-0.25f, -0.28125f, -0.3125f, -0.34375f, -0.375f, -0.40625f, -0.4375f, -0.46875f,
-0.5f, -0.5625f, -0.625f, -0.6875f, -0.75f, -0.8125f, -0.875f, -0.9375f,
-1.0f, -1.125f, -1.25f, -1.375f, -1.5f, -1.625f, -1.75f, -1.875f,
-2.0f, -2.25f, -2.5f, -2.75f, -3.0f, -3.25f, -3.5f, -3.75f,
-4.0f, -4.5f, -5.0f, -5.5f, -6.0f, -6.5f, -7.0f, -7.5f,
-8.0f, -9.0f, -10.0f, -11.0f, -12.0f, -13.0f, -14.0f, -15.0f,
-16.0f, -18.0f, -20.0f, -22.0f, -24.0f, -26.0f, -28.0f, -30.0f,
-32.0f, -36.0f, -40.0f, -44.0f, -48.0f, -52.0f, -56.0f, -60.0f,
-64.0f, -72.0f, -80.0f, -88.0f, -96.0f, -104.0f, -112.0f, -120.0f,
-128.0f, -144.0f, -160.0f, -176.0f, -192.0f, -208.0f, -224.0f, -240.0f,
-256.0f, -288.0f, -320.0f, -352.0f, -384.0f, -416.0f, -448.0f, -f32_qnan};
using f8m4e3_params = std::tuple<int, float>;
class F8E4M3PTest : public testing::TestWithParam<f8m4e3_params> {};
INSTANTIATE_TEST_SUITE_P(convert,
F8E4M3PTest,
testing::ValuesIn(enumerate(exp_floats)),
testing::PrintToStringParamName());
TEST_P(F8E4M3PTest, f8_bits_to_f32) {
const auto& params = GetParam();
const auto& exp_value = std::get<1>(params);
const auto f8 = ov::float8_e4m3::from_bits(std::get<0>(params));
if (std::isnan(exp_value)) {
EXPECT_TRUE(std::isnan(static_cast<float>(f8)));
} else {
EXPECT_EQ(static_cast<float>(f8), exp_value);
}
}
TEST_P(F8E4M3PTest, f32_to_f8_bits) {
const auto& params = GetParam();
const auto& exp_value = std::get<0>(params);
const auto& value = std::get<1>(params);
const auto f8 = ov::float8_e4m3(value);
EXPECT_EQ(f8.to_bits(), exp_value);
}
} // namespace test
} // namespace ov