* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* @brief Float8 E8M0 标杆值测试
*
* 格式: 8-bit exponent only, no sign/mantissa, bias=127
* 参考: OCP Microscaling Formats (MX) v1.0 - Scale Factor Format
*
* 测试方向:
* 1. float -> 位模式: 验证 float 输入转换后的位表示
* 2. 位模式 -> float: 验证位模式的语义解释是否符合 OCP 规范
*/
#include <cmath>
#include <limits>
#include "gtest/gtest.h"
#include "float_benchmark_data.h"
#include "opdev/float8_e8m0.h"
namespace op {
namespace benchmark {
static Float8E8M0 FromBits(uint8_t bits) { return Float8E8M0(bits, Float8E8M0::FromBits()); }
static constexpr BenchmarkCase<Float8E8M0> kE8M0_BoundaryCases[] = {
{1.0f, 0x7F, 1.0f, 0.0f, "one (2^0)", "OCP"}, {2.0f, 0x80, 2.0f, 0.0f, "two (2^1)", "OCP"},
{0.5f, 0x7E, 0.5f, 0.0f, "half (2^-1)", "OCP"}, {4.0f, 0x81, 4.0f, 0.0f, "four (2^2)", "OCP"},
{0.25f, 0x7D, 0.25f, 0.0f, "quarter (2^-2)", "OCP"},
};
class Float8E8M0Benchmark : public testing::Test {
protected:
void RunBenchmark(const BenchmarkCase<Float8E8M0>& tc)
{
Float8E8M0 result(tc.input);
EXPECT_EQ(result.value, tc.expected_bits) << "Input: " << tc.input << ", Desc: " << tc.description;
float actual = static_cast<float>(result);
EXPECT_NEAR(actual, tc.expected_value, tc.tolerance) << "Desc: " << tc.description;
}
};
TEST_F(Float8E8M0Benchmark, BoundaryValues)
{
for (const auto& tc : kE8M0_BoundaryCases) {
RunBenchmark(tc);
}
}
TEST_F(Float8E8M0Benchmark, PowerOfTwoOnly)
{
Float8E8M0 val(3.0f);
float result = static_cast<float>(val);
EXPECT_TRUE(result == 2.0f || result == 4.0f);
}
TEST_F(Float8E8M0Benchmark, LargePowerOfTwo)
{
Float8E8M0 large_val(256.0f);
float result = static_cast<float>(large_val);
EXPECT_FLOAT_EQ(result, 256.0f);
}
TEST_F(Float8E8M0Benchmark, SmallPowerOfTwo)
{
Float8E8M0 small_val(0.125f);
float result = static_cast<float>(small_val);
EXPECT_FLOAT_EQ(result, 0.125f);
}
TEST_F(Float8E8M0Benchmark, NegativeValueHandling)
{
Float8E8M0 neg_val(-1.0f);
EXPECT_TRUE(neg_val.IsNaN());
}
TEST_F(Float8E8M0Benchmark, NaNHandling)
{
Float8E8M0 nan_val(std::nanf(""));
EXPECT_TRUE(nan_val.IsNaN());
EXPECT_EQ(nan_val.value, 0xFF);
}
TEST_F(Float8E8M0Benchmark, OcpSpec_PowersOfTwo)
{
Float8E8M0 one = FromBits(0x7F);
EXPECT_EQ(one.value, 0x7F);
EXPECT_FLOAT_EQ(static_cast<float>(one), 1.0f);
Float8E8M0 two = FromBits(0x80);
EXPECT_EQ(two.value, 0x80);
EXPECT_FLOAT_EQ(static_cast<float>(two), 2.0f);
Float8E8M0 half = FromBits(0x7E);
EXPECT_EQ(half.value, 0x7E);
EXPECT_FLOAT_EQ(static_cast<float>(half), 0.5f);
}
TEST_F(Float8E8M0Benchmark, OcpSpec_NaN)
{
Float8E8M0 nan_val = FromBits(0xFF);
EXPECT_EQ(nan_val.value, 0xFF);
EXPECT_TRUE(nan_val.IsNaN());
}
TEST_F(Float8E8M0Benchmark, OcpSpec_MaxMin)
{
Float8E8M0 max_val = FromBits(0xFE);
EXPECT_EQ(max_val.value, 0xFE);
EXPECT_FLOAT_EQ(static_cast<float>(max_val), std::ldexp(1.0f, 127));
Float8E8M0 zero_val = FromBits(0x00);
EXPECT_EQ(zero_val.value, 0x00);
EXPECT_FLOAT_EQ(static_cast<float>(zero_val), 0.0f);
Float8E8M0 min_pos = FromBits(0x01);
EXPECT_EQ(min_pos.value, 0x01);
EXPECT_FLOAT_EQ(static_cast<float>(min_pos), std::ldexp(1.0f, -126));
}
TEST_F(Float8E8M0Benchmark, OcpSpec_TypicalBitPatterns)
{
EXPECT_FLOAT_EQ(static_cast<float>(FromBits(0x7F)), 1.0f);
EXPECT_FLOAT_EQ(static_cast<float>(FromBits(0x80)), 2.0f);
EXPECT_FLOAT_EQ(static_cast<float>(FromBits(0x81)), 4.0f);
EXPECT_FLOAT_EQ(static_cast<float>(FromBits(0x82)), 8.0f);
EXPECT_FLOAT_EQ(static_cast<float>(FromBits(0x7D)), 0.25f);
EXPECT_FLOAT_EQ(static_cast<float>(FromBits(0x7C)), 0.125f);
}
}
}