Files
cKit/Foundation/c_float.t.c
T
2026-08-29 11:39:28 +08:00

382 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "c_float.h"
#include <stdlib.h>
#include <stdio.h>
#include <c_Test.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* @brief 辅助工具:将 C 语言原生双精度 double 转换为 64 位原始位码 (uint64_t)
*/
static uint64_t to_raw64(double d) {
c_Double_t u;
u.d = d;
return u.raw;
}
/**
* @brief 辅助工具:将 64 位原始位码 (uint64_t) 还原为 C 语言原生双精度 double
*/
static double to_double(uint64_t raw) {
c_Double_t u;
u.raw = raw;
return u.d;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 用例 1:测试 Float 分类状态识别函数(IsZero, IsInf, IsNAN 等基本位打包判定)
void test_float_classification_and_pack() {
c_Float_t val;
// 1. 测试 Pack 是否能准确组装成标准浮点位
// 符号=0, 指数=127(偏移后为0), 尾数=0 -> 应该代表 1.0f
uint32_t packed = c_Float_Pack(0, 127, 0);
val.raw = packed;
ASSERT_MSG(val.f == 1.0f, "c_Float_Pack failed to assemble 1.0f");
// 2. 测试 正负零 (0.0f 和 -0.0f)
val.f = 0.0f;
ASSERT_MSG(c_Float_IsZero(val.raw) == true, "0.0f should be identified as zero");
val.f = -0.0f;
ASSERT_MSG(c_Float_IsZero(val.raw) == true, "-0.0f should be identified as zero");
// 3. 测试 正负无穷大 (Inf)
val.raw = C_FLOAT_POS_INF;
ASSERT_MSG(c_Float_IsInf(val.raw) == true, "POS_INF must be Inf");
ASSERT_MSG(c_Float_IsPosInf(val.raw) == true, "POS_INF must be PosInf");
val.raw = C_FLOAT_NEG_INF;
ASSERT_MSG(c_Float_IsInf(val.raw) == true, "NEG_INF must be Inf");
ASSERT_MSG(c_Float_IsNegInf(val.raw) == true, "NEG_INF must be NegInf");
// 4. 测试 NaN(指数全为1,尾数不为0)
val.raw = C_FLOAT_EXP_MASK | 0x00000001U; // 制造一个 NaN
ASSERT_MSG(c_Float_IsNAN(val.raw) == 1, "Should be recognized as NaN");
val.raw = C_FLOAT_POS_INF; // 无穷大的尾数是0,不属于 NaN
ASSERT_MSG(c_Float_IsNAN(val.raw) == 0, "Infinity is NOT NaN");
}
// 用例 2:测试 Float 基础数学四则运算(数值运算准确性)
void test_float_math_operations() {
c_Float_t res, out_add, out_sub, out_mul, out_div;
c_Float_t a, b;
a.f = 5.5f;
b.f = 2.25f;
// 1. 加法测试 5.5 + 2.25 = 7.75
out_add.raw = c_Float_Add(a.raw, b.raw);
res.f = 7.75f;
ASSERT_INT_EQ_MSG(res.raw, out_add.raw, "Soft-Float Add failed (5.5 + 2.25)");
// 2. 减法测试 5.5 - 2.25 = 3.25
out_sub.raw = c_Float_Sub(a.raw, b.raw);
res.f = 3.25f;
ASSERT_INT_EQ_MSG(res.raw, out_sub.raw, "Soft-Float Sub failed (5.5 - 2.25)");
// 3. 乘法测试 5.5 * 2.25 = 12.375
out_mul.raw = c_Float_Mul(a.raw, b.raw);
res.f = 12.375f;
ASSERT_INT_EQ_MSG(res.raw, out_mul.raw, "Soft-Float Mul failed (5.5 * 2.25)");
// 4. 除法测试 5.5 / 2.25 = 2.444444... (通过联合体转换进行交叉对比)
out_div.raw = c_Float_Div(a.raw, b.raw);
float expected_div = 5.5f / 2.25f;
uint32_t expected_raw = ((c_Float_t){.f = expected_div}).raw;
// 经过软除法精度升级后,这里预期可以做到每一个二进制位都完全绝对对齐(0 ULP 误差)
ASSERT_INT_EQ_MSG((int)expected_raw, (int)out_div.raw, "Soft-Float Div Round-to-Nearest-Even failed to align with hardware bits");
}
void test_float_div_complete() {
c_Float_t a, b, out;
// -------------------------------------------------------------
// 测试 1:常规数值除法 (15.5 / 2.0 = 7.75)
// -------------------------------------------------------------
a.f = 15.5f;
b.f = 2.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(fabsf(7.75f - out.f)<FLT_EPSILON, "Regular division failed (15.5 / 2.0)");
// -------------------------------------------------------------
// 测试 2:符号位正确结合测试 (-15.5 / 2.0 = -7.75)
// -------------------------------------------------------------
a.f = -15.5f;
b.f = 2.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(fabsf(-7.75f - out.f) < FLT_EPSILON, "Signed division failed (-15.5 / 2.0)");
// -------------------------------------------------------------
// 测试 3:有限数除以 0.0 —— 预期触发 ±Inf 拦截 (X / 0 = Inf)
// -------------------------------------------------------------
a.f = 5.25f;
b.f = 0.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(c_Float_IsPosInf(out.raw), "5.25 / 0.0 must yield Positive Infinity");
a.f = -5.25f;
b.f = 0.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(c_Float_IsNegInf(out.raw), "-5.25 / 0.0 must yield Negative Infinity");
// -------------------------------------------------------------
// 测试 4:零除以零 边界熔断 —— 预期触发 NaN (0.0 / 0.0 = NaN)
// -------------------------------------------------------------
a.f = 0.0f;
b.f = 0.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(c_Float_IsNAN(out.raw), "0.0 / 0.0 must result in NaN");
// -------------------------------------------------------------
// 测试 5:无穷大除以无穷大 边界熔断 —— 预期触发 NaN (Inf / Inf = NaN)
// -------------------------------------------------------------
out.raw = c_Float_Div(C_FLOAT_POS_INF, C_FLOAT_POS_INF);
ASSERT_MSG(c_Float_IsNAN(out.raw), "PosInf / PosInf must result in NaN");
out.raw = c_Float_Div(C_FLOAT_POS_INF, C_FLOAT_NEG_INF);
ASSERT_MSG(c_Float_IsNAN(out.raw), "PosInf / NegInf must result in NaN");
// -------------------------------------------------------------
// 测试 6:精确偶数舍入测试(循环除法产生除不尽的无限循环小数)
// -------------------------------------------------------------
a.f = 1.0f;
b.f = 3.0f; // 1.0 / 3.0 = 0.33333333...
out.raw = c_Float_Div(a.raw, b.raw);
// 提取系统原生结果进行高精密比对(ULP 误差必须为 0)
float native_expected = 1.0f / 3.0f;
c_Float_t native_val = {.f = native_expected};
ASSERT_INT_EQ_MSG((int)native_val.raw, (int)out.raw, "1.0 / 3.0 Round-to-Nearest-Even failed");
}
// 用例 3:测试 Float IEEE 754 软加法器/乘法器的特殊边界极限(这最容易导致软浮点数崩溃或死循环)
void test_float_edge_cases() {
c_Float_t a, b, out;
// 1. 任何数与 NaN 运算都必须得到 NaN
a.raw = C_FLOAT_EXP_MASK | 0x1234U; // NaN
b.f = 5.0f;
out.raw = c_Float_Add(a.raw, b.raw);
ASSERT_MSG(c_Float_IsNAN(out.raw) == 1, "NaN + Number must result in NaN");
// 2. 边界:正无穷大 + 负无穷大 应该得到 NaN (Inf - Inf = NaN)
out.raw = c_Float_Add(C_FLOAT_POS_INF, C_FLOAT_NEG_INF);
ASSERT_MSG(c_Float_IsNAN(out.raw), "PosInf + NegInf must result in NaN");
// 3. 边界:有限非零数除以 0 应该触发除零异常得到 无穷大 (X / 0 = Inf)
a.f = 3.5f;
b.f = 0.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(c_Float_IsPosInf(out.raw), "3.5 / 0.0 must result in Positive Infinity");
// 4. 边界:0 / 0 应该产生 NaN
a.f = 0.0f;
b.f = 0.0f;
out.raw = c_Float_Div(a.raw, b.raw);
ASSERT_MSG(c_Float_IsNAN(out.raw) == 1, "0.0 / 0.0 must result in NaN");
}
// 用例 4:测试 Double 的数值运算及大小比较行为 (c_Double_Cmp)
void test_double_operations_and_compare() {
c_Double_t a, b, out;
// 1. 测试基础双精度加法
a.d = 123456789.12345;
b.d = 987654321.98765;
out.raw = c_Double_Add(a.raw, b.raw);
c_Double_t expected;
expected.d = a.d + b.d;
int64_t diff = (int64_t)out.raw - (int64_t)expected.raw;
ASSERT_MSG(labs(diff) <= 1, "c_Double_Add precision verification mismatched");
// 2. 测试 c_Double_Cmp 逻辑
// 规定返回值规范:a < b 返回负数,a == b 返回 0a > b 返回正数
a.d = 100.5;
b.d = 200.5;
ASSERT_MSG(c_Double_Cmp(a.raw, b.raw) < 0, "100.5 should be less than 200.5");
ASSERT_MSG(c_Double_Cmp(b.raw, a.raw) > 0, "200.5 should be greater than 100.5");
ASSERT_MSG(c_Double_Cmp(a.raw, a.raw) == 0, "100.5 should be equal to itself");
// 3. 原生内联函数的包装测试 (c_double_cmp)
ASSERT_MSG(c_double_cmp(10.0, 20.0) < 0, "Inline double compare wrapper failed");
}
void test_float_isnan_pure_bits() {
// 场景 1:制造标准常规数值(如 1.0f)—— 预期:非 NaN (0)
// 符号=0, 指数=127, 尾数=0
uint32_t normal_num = c_Float_Pack(0, 127, 0);
ASSERT_INT_EQ_MSG(0, c_Float_IsNAN(normal_num), "Normal number 1.0f must NOT be NaN");
// 场景 2:正无穷大 (C_FLOAT_POS_INF) —— 预期:非 NaN (0)
// 它的指数全为 1,但尾数严格为 0
ASSERT_INT_EQ_MSG(0, c_Float_IsNAN(C_FLOAT_POS_INF), "Positive Infinity must NOT be NaN");
ASSERT_INT_EQ_MSG(0, c_Float_IsNAN(C_FLOAT_NEG_INF), "Negative Infinity must NOT be NaN");
// 场景 3:制造一个最微小的 Quiet NaN (QNaN) —— 预期:是 NaN (1)
// 指数全为 1 (0xFF),尾数最高位为 1 (0x400000)
uint32_t qnan_bits = c_Float_Pack(0, 0xFF, 0x400000U);
ASSERT_MSG(c_Float_IsNAN(qnan_bits), "Quiet NaN bits must be recognized as NaN");
// 场景 4:制造一个最微小的 Signaling NaN (SNaN) —— 预期:是 NaN (1)
// 指数全为 1 (0xFF),尾数最低位为 1 (0x000001)
uint32_t snan_bits = c_Float_Pack(0, 0xFF, 0x000001U);
ASSERT_MSG(c_Float_IsNAN(snan_bits), "Signaling NaN bits must be recognized as NaN");
// 场景 5:测试带有符号位的 NaN (负 NaN) —— 预期:是 NaN (1)
// IEEE 754 规范中,NaN 的符号位不影响它是 NaN 的事实
uint32_t neg_nan_bits = c_Float_Pack(1, 0xFF, 0x7FFFFFU);
ASSERT_MSG(c_Float_IsNAN(neg_nan_bits), "Negative NaN bits must also be recognized as NaN");
}
void test_float_inf_plus_neginf() {
// 1. 获取正无穷大与负无穷大的位表示
uint32_t pos_inf = C_FLOAT_POS_INF; // 0x7F800000
uint32_t neg_inf = C_FLOAT_NEG_INF; // 0xFF800000
// 2. 执行待测的软浮点加法:(+Inf) + (-Inf)
uint32_t result_raw = c_Float_Add(pos_inf, neg_inf);
// 3. 核心断言:结果必须是 NaN
// 使用 c_Float_IsNAN 验证其特征是否为:指数全 1,尾数非 0
ASSERT_MSG(c_Float_IsNAN(result_raw), "IEEE 754 standard: (+Inf) + (-Inf) must produce NaN");
// 4. 反向验证:它绝对不能再被误判为任何形式的无穷大或零
ASSERT_MSG(!c_Float_IsInf(result_raw), "Result NaN must not be classified as Infinity");
ASSERT_MSG(!c_Float_IsZero(result_raw), "Result NaN must not be classified as Zero");
// 5. 跨双精度对称验证:(+Inf) + (-Inf) 同样适用于 64 位双精度
uint64_t d_pos_inf = C_DOUBLE_POS_INF;
uint64_t d_neg_inf = C_DOUBLE_NEG_INF;
uint64_t d_result_raw = c_Double_Add(d_pos_inf, d_neg_inf);
ASSERT_MSG(c_Double_IsNAN(d_result_raw), "IEEE 754 standard: Double (+Inf) + (-Inf) must produce NaN");
}
void test_double_mul_and_div_complete() {
c_Double_t da, db, dout;
// -----------------------------------------------------------------
// 测试 1:验证跨 64 位大整数相乘的精确偶数舍入
// -----------------------------------------------------------------
da.d = 1.23456789012345;
db.d = -9.87654321098765;
dout.raw = c_Double_Mul(da.raw, db.raw);
double expected_mul = 1.23456789012345 * -9.87654321098765;
c_Double_t native_mul = {.d = expected_mul};
// 【终极断言升级】:杜绝 int 转换截断,对双精度 64 位全局原始编码进行无差错硬核对齐
ASSERT_MSG(native_mul.raw == dout.raw,
"c_Double_Mul 64-bit full-width precision failed to align with hardware FPU");
// -----------------------------------------------------------------
// 测试 2:验证双精度无限循环小数除法的长窗口状态机精度
// -----------------------------------------------------------------
da.d = 1.0;
db.d = 3.0; // 1.0 / 3.0
dout.raw = c_Double_Div(da.raw, db.raw);
double expected_div = 1.0 / 3.0;
c_Double_t native_div = {.d = expected_div};
ASSERT_INT_EQ_MSG((int)native_div.parts.fraction, (int)dout.parts.fraction, "c_Double_Div bit-level precision error at 1.0/3.0");
// -----------------------------------------------------------------
// 测试 3:验证双精度 0.0 与 无穷大的复合熔断边界
// -----------------------------------------------------------------
// 边界 A0.0 * Inf -> 必须返回 NaN
uint64_t zero_mul_inf = c_Double_Mul(to_raw64(0.0), C_DOUBLE_POS_INF);
ASSERT_MSG(c_Double_IsNAN(zero_mul_inf), "Double 0.0 * +Inf must result in NaN");
// 边界 B0.0 / 0.0 -> 必须返回 NaN
uint64_t zero_div_zero = c_Double_Div(to_raw64(0.0), to_raw64(-0.0));
ASSERT_MSG(c_Double_IsNAN(zero_div_zero), "Double 0.0 / -0.0 must result in NaN");
// 边界 C:常规有限双精度数除以 0.0 -> 产生无穷大
da.d = -55.5;
uint64_t div_zero = c_Double_Div(to_raw64(da.d), to_raw64(0.0));
ASSERT_MSG(c_Double_IsNegInf(div_zero), "Negative double divided by 0.0 must result in -Inf");
}
void test_double_add_complete() {
c_Double_t da, db, dout;
// -----------------------------------------------------------------
// 测试 1:常规双精度数值加减(50.25 + 25.5 = 75.75
// -----------------------------------------------------------------
da.d = 50.25;
db.d = 25.5;
dout.raw = c_Double_Add(da.raw, db.raw);
// 使用真值进行精度验证
ASSERT_MSG(fabs(dout.d - 75.75) < 1e-9, "Regular Double Add numerical verification failed");
// -----------------------------------------------------------------
// 测试 2:验证正负无穷大冲抵边界熔断(+Inf + -Inf = NaN
// -----------------------------------------------------------------
uint64_t res_nan = c_Double_Add(C_DOUBLE_POS_INF, C_DOUBLE_NEG_INF);
ASSERT_MSG(c_Double_IsNAN(res_nan), "IEEE 754: Double (+Inf) + (-Inf) must produce NaN");
// -----------------------------------------------------------------
// 测试 3:验证异号数值完全抵消(5.5 + -5.5 = +0.0
// -----------------------------------------------------------------
da.d = 5.5;
db.d = -5.5;
dout.raw = c_Double_Add(da.raw, db.raw);
// 验证返回的是否是干净的、符号位为0的正零 (0x0000000000000000)
ASSERT_INT_EQ_MSG(0, (int)dout.raw, "Opposite numbers sum must strictly result in +0.0 bits");
// -----------------------------------------------------------------
// 测试 4:大跨度对阶精度测试(1.0 + 1e-17 触发全移出边界)
// -----------------------------------------------------------------
da.d = 1.0;
db.d = 1e-17; // 这个值太小了,在双精度 53 位尾数对阶时会被完全移出去,但会触发 sticky 位置 1
dout.raw = c_Double_Add(da.raw, db.raw);
// 按照偶数舍入规则,sticky=1GRS=001 <= 4,将被舍去,结果应该严格保持为 1.0
ASSERT_MSG(dout.d == 1.0, "Large exponent gap shift processing failed");
// -----------------------------------------------------------------
// 测试 5:向最近偶数舍入的 0 ULP 硬件级绝对对齐校验
// -----------------------------------------------------------------
da.d = 1.23456789012345;
db.d = 9.87654321098765;
dout.raw = c_Double_Add(da.raw, db.raw);
double native_expected = 1.23456789012345 + 9.87654321098765;
c_Double_t native_val = {.d = native_expected};
// 通过对比尾数域,验证是否做到了 100% 硬件位对齐
ASSERT_INT_EQ_MSG((int)native_val.parts.fraction, (int)dout.parts.fraction,
"Soft-Double Add failed to align with hardware FPU bits");
}
int main(int argc, char** argv){
TEST_START(Starting Unit Tests);
// 运行需要内存环境的用例
RUN_TEST(test_float_classification_and_pack);
RUN_TEST(test_float_math_operations);
RUN_TEST(test_float_edge_cases);
RUN_TEST(test_double_operations_and_compare);
RUN_TEST(test_float_isnan_pure_bits);
RUN_TEST(test_float_inf_plus_neginf);
RUN_TEST(test_float_div_complete);
RUN_TEST(test_double_mul_and_div_complete);
RUN_TEST(test_double_add_complete);
// 打印最终统计报告
TEST_REPORT();
RETURN_TEST_STATUS;
}