测试用例

This commit is contained in:
2026-08-29 11:40:02 +08:00
parent 8fd65b0de0
commit 84ebd52c24
3 changed files with 1126 additions and 0 deletions
+915
View File
@@ -0,0 +1,915 @@
#include <c_Float.h>
#include <c_Memory.h>
uint32_t c_Float_Add(uint32_t a, uint32_t b) {
// 1. 提取特殊狀態與快速零值返回
if ((a & ~C_FLOAT_SIGN_MASK) == 0) return b;
if ((b & ~C_FLOAT_SIGN_MASK) == 0) return a;
uint32_t sign_a = a & C_FLOAT_SIGN_MASK;
uint32_t sign_b = b & C_FLOAT_SIGN_MASK;
int32_t exp_a = (int32_t)((a & C_FLOAT_EXP_MASK) >> 23);
int32_t exp_b = (int32_t)((b & C_FLOAT_EXP_MASK) >> 23);
uint32_t frac_a = a & C_FLOAT_FRAC_MASK;
uint32_t frac_b = b & C_FLOAT_FRAC_MASK;
// ==========================================
// 修复 Bug 1:严格遵循 IEEE 754 的 NaN / Inf 拦截规则
// ==========================================
if (exp_a == 255 || exp_b == 255) {
// 条件 1.1:若任一为真正的 NaN(指数全1,尾数不为0),则传播 NaN
if ((exp_a == 255 && frac_a != 0) || (exp_b == 255 && frac_b != 0)) {
return 0x7FC00000U; // 返回 Quiet NaN
}
// 条件 1.2:若两者都是无穷大,且符号相反(+Inf + -Inf),属于未定义,必须强行返回 NaN
if (exp_a == 255 && exp_b == 255 && sign_a != sign_b) {
return 0x7FC00000U; // 熔断返回 NaN
}
// 条件 1.3:普通的无穷大传播(如 Inf + 有限数 = Inf
return (exp_a == 255) ? a : b;
}
// ==========================================
// 修复 Bug 3:更正非规格化数的隐藏位逻辑(exp==0 时隐藏位是 0,且不能左移)
// ==========================================
frac_a = (exp_a == 0) ? frac_a : (frac_a | C_FLOAT_HIDDEN_BIT);
frac_b = (exp_b == 0) ? frac_b : (frac_b | C_FLOAT_HIDDEN_BIT);
if (exp_a == 0) exp_a = 1;
if (exp_b == 0) exp_b = 1;
// 开辟 GRS 保护位空间
frac_a <<= 3;
frac_b <<= 3;
int32_t exp_res = exp_a;
uint32_t sticky = 0;
// 3. 對階(Align Exponents
if (exp_a > exp_b) {
int32_t shift = exp_a - exp_b;
// 修复 Bug 2:防止大跨度对阶时左移掩码溢出未定义行为
if (shift >= 27) {
sticky = (frac_b != 0);
frac_b = 0;
} else {
sticky = (frac_b & ~((~0U) << shift)) != 0;
frac_b >>= shift;
}
frac_b |= sticky;
exp_res = exp_a;
} else if (exp_b > exp_a) {
int32_t shift = exp_b - exp_a;
// 修复 Bug 2:同理防护
if (shift >= 27) {
sticky = (frac_a != 0);
frac_a = 0;
} else {
sticky = (frac_a & ~((~0U) << shift)) != 0;
frac_a >>= shift;
}
frac_a |= sticky;
exp_res = exp_b;
}
// 4. 尾數運算
uint32_t sign_res;
uint32_t frac_res;
if (sign_a == sign_b) {
sign_res = sign_a;
frac_res = frac_a + frac_b;
} else {
if (frac_a >= frac_b) {
sign_res = sign_a;
frac_res = frac_a - frac_b;
} else {
sign_res = sign_b;
frac_res = frac_b - frac_a;
}
if (frac_res == 0) return 0x00000000U; // 正负抵消返回标准 +0.0
}
// 5. 規格化
if (frac_res & (1U << 27)) {
// 修复 Bug 4:修正右移 1 位时的 Sticky 保留逻辑
uint32_t lost_bit = frac_res & 1U;
frac_res >>= 1;
frac_res |= lost_bit;
exp_res++;
} else {
while (!(frac_res & (1U << 26)) && exp_res > 1) {
frac_res <<= 1;
exp_res--;
}
if (!(frac_res & (1U << 26)) && exp_res == 1) {
exp_res = 0;
}
}
// 6. 溢出至無限大檢查
if (exp_res >= 255) {
return sign_res | C_FLOAT_EXP_MASK;
}
// 7. 向最接近偶數捨入(Round-to-Nearest-Even
uint32_t round_bits = frac_res & 7U;
frac_res >>= 3;
if ((round_bits > 4) || ((round_bits == 4) && (frac_res & 1U))) {
frac_res++;
if (frac_res & (1U << 24)) {
frac_res >>= 1;
exp_res++;
if (exp_res >= 255) return sign_res | C_FLOAT_EXP_MASK;
}
}
if (exp_res != 0) {
frac_res &= C_FLOAT_FRAC_MASK;
}
// 8. 拼裝返回
return sign_res | ((uint32_t)exp_res << 23) | frac_res;
}
uint32_t c_Float_Mul(uint32_t a, uint32_t b) {
// 使用你定义的快捷提取宏
uint32_t sign_a = C_FLOAT_GET_SIGN(a);
uint32_t sign_b = C_FLOAT_GET_SIGN(b);
uint32_t exp_a = C_FLOAT_GET_EXP(a);
uint32_t exp_b = C_FLOAT_GET_EXP(b);
uint32_t frac_a = C_FLOAT_GET_FRAC(a);
uint32_t frac_b = C_FLOAT_GET_FRAC(b);
uint32_t sign_res = sign_a ^ sign_b; // 异或决定结果符号
// ==========================================
// 边界条件 1:处理 NaN 和 无穷大 (Inf) 的传播与熔断
// ==========================================
if (exp_a == 255 || exp_b == 255) {
// 条件 1.1:若任一输入为真正的 NaN,直接传播 Quiet NaN
if ((exp_a == 255 && frac_a != 0) || (exp_b == 255 && frac_b != 0)) {
return 0x7FC00000U;
}
// 条件 1.20.0 * 无穷大 (0.0 * Inf) 属于未定义,必须强行熔断返回 NaN
bool is_a_zero = (exp_a == 0 && frac_a == 0);
bool is_b_zero = (exp_b == 0 && frac_b == 0);
if ((exp_a == 255 && is_b_zero) || (exp_b == 255 && is_a_zero)) {
return 0x7FC00000U; // 熔断返回 NaN
}
// 条件 1.3:普通的无穷大传播(Inf * 有限非零数 = Inf)
return c_Float_Pack(sign_res, 255, 0);
}
// ==========================================
// 边界条件 2:处理纯零快速返回
// ==========================================
if ((exp_a == 0 && frac_a == 0) || (exp_b == 0 && frac_b == 0)) {
return c_Float_Pack(sign_res, 0, 0); // 0.0 * 有限数 -> 产生带有正确符号的 ±0.0
}
// ==========================================
// 2. 补齐隐藏位并处理非规格化数
// ==========================================
frac_a = (exp_a == 0) ? frac_a : (frac_a | C_FLOAT_HIDDEN_BIT);
frac_b = (exp_b == 0) ? frac_b : (frac_b | C_FLOAT_HIDDEN_BIT);
if (exp_a == 0) exp_a = 1;
if (exp_b == 0) exp_b = 1;
int32_t exp_res = (int32_t)exp_a + (int32_t)exp_b - C_FLOAT_EXP_BIAS;
// ==========================================
// 3. 执行核心尾数相乘(24位 * 24位 = 48位超长整数)
// ==========================================
uint64_t prod = (uint64_t)frac_a * (uint64_t)frac_b;
// ==========================================
// 4. 将 48 位乘积向右压缩,腾出低 3 位作为 GRS 保护位空间
// ==========================================
// 正常规格化数相乘后,结果 `prod` 的最高位 1 应该在第 46 位或第 47 位(从0数起)。
// 为了最终留下 24 位标准尾数和 3 位保护位,我们需要让规格化后的目标保留在 27 位。
// 因此,我们先固定将原本 48 位的低 20 位挤出去,并把这 20 位中任意的 1 凝聚为 Sticky 位。
uint32_t sticky = (prod & 0xFFFFF) != 0;
uint32_t frac_res = (uint32_t)(prod >> 20); // 压缩至大约 27~28 位
frac_res |= sticky; // 将物理挤出去的所有小数信息固化在最低位
// ==========================================
// 5. 规格化积(Normalization
// ==========================================
// 如果最高有效位 1 溢出到了第 27 位 (1U << 27),说明乘积结果 >= 2.0,需要右移 1 位,指数加 1
if (frac_res & (1U << 27)) {
uint32_t lost_bit = frac_res & 1U;
frac_res >>= 1;
frac_res |= lost_bit; // 保持最低位 Sticky 不丢失
exp_res++;
} else {
// 如果隐藏位没落到第 26 位,说明乘积较小 (< 1.0),需要左移直到最高有效位 1 回归第 26 位
while (!(frac_res & (1U << 26)) && exp_res > 1) {
uint32_t sticky_backup = frac_res & 1U;
frac_res <<= 1;
frac_res |= sticky_backup; // 锁死 Sticky 状态
exp_res--;
}
// 下溢退化为非规格化数
if (!(frac_res & (1U << 26)) && exp_res == 1) {
exp_res = 0;
}
}
// 上下溢出安全拦截
if (exp_res >= 255) return c_Float_Pack(sign_res, 255, 0); // 上溢至 ±Inf
if (exp_res <= 0) return c_Float_Pack(sign_res, 0, 0); // 下溢至 ±0.0
// ==========================================
// 6. 激活 IEEE 754 标准:向最接近偶数舍入(Round-to-Nearest-Even
// ==========================================
uint32_t round_bits = frac_res & 7U; // 捕获低 3 位的 GRS 数据
frac_res >>= 3; // 移除保护位,回归标准 24 位尾数
if ((round_bits > 4) || ((round_bits == 4) && (frac_res & 1U))) {
frac_res++;
// 舍入导致的二次溢出处理
if (frac_res & (1U << 24)) {
frac_res >>= 1;
exp_res++;
if (exp_res >= 255) return c_Float_Pack(sign_res, 255, 0);
}
}
// 剥离规格化数中用于拼装的高位隐藏位 1
if (exp_res != 0) {
frac_res &= C_FLOAT_FRAC_MASK;
}
// 7. 最终位打包返回
return c_Float_Pack(sign_res, exp_res, frac_res);
}
uint32_t c_Float_Div(uint32_t a, uint32_t b) {
// 使用你定义的快捷提取宏
uint32_t sign_a = C_FLOAT_GET_SIGN(a);
uint32_t sign_b = C_FLOAT_GET_SIGN(b);
uint32_t exp_a = C_FLOAT_GET_EXP(a);
uint32_t exp_b = C_FLOAT_GET_EXP(b);
uint32_t frac_a = C_FLOAT_GET_FRAC(a);
uint32_t frac_b = C_FLOAT_GET_FRAC(b);
uint32_t sign_res = sign_a ^ sign_b;
// =========================================================================
// 修复 Bug 1 & 2:严密拦截 IEEE 754 规定的所有 NaN、Inf、特殊零边界
// =========================================================================
// 1. 拦截输入本身为 NaN 或 无穷大 (exp == 255) 的异常传播
if (exp_a == 255 || exp_b == 255) {
// 条件 A: 任一为真正的 NaN(尾数非0),直接传播 Quiet NaN
if ((exp_a == 255 && frac_a != 0) || (exp_b == 255 && frac_b != 0)) {
return 0x7FC00000U;
}
// 条件 B: 无穷大除以无穷大 (Inf / Inf) 属于严重未定义,强熔断返回 NaN
if (exp_a == 255 && exp_b == 255) {
return 0x7FC00000U;
}
// 条件 C: 无穷大除以普通有限数 = 无穷大
if (exp_a == 255) {
return c_Float_Pack(sign_res, 255, 0);
}
// 条件 D: 普通有限数除以无穷大 = 0
return c_Float_Pack(sign_res, 0, 0);
}
// 2. 剥离符号位,准确捕捉纯零值 (+/-0.0) 状态下的防御拦截
bool is_a_zero = (exp_a == 0 && frac_a == 0);
bool is_b_zero = (exp_b == 0 && frac_b == 0);
if (is_b_zero) {
// 0.0 / 0.0 必须熔断返回 NaN
if (is_a_zero) {
return 0x7FC00000U;
}
// 有限数 / 0.0 -> 产生标准的 ±Infinity
return c_Float_Pack(sign_res, 255, 0);
}
// 0.0 / 非零数 -> 产生标准的 ±0.0
if (is_a_zero) {
return c_Float_Pack(sign_res, 0, 0);
}
// =========================================================================
// 修复 Bug 4:正确补齐隐藏位(非规格化数的隐藏位是 0)
// =========================================================================
frac_a = (exp_a == 0) ? frac_a : (frac_a | C_FLOAT_HIDDEN_BIT);
frac_b = (exp_b == 0) ? frac_b : (frac_b | C_FLOAT_HIDDEN_BIT);
if (exp_a == 0) exp_a = 1;
if (exp_b == 0) exp_b = 1;
int32_t exp_res = (int32_t)exp_a - (int32_t)exp_b + C_FLOAT_EXP_BIAS;
// =========================================================================
// 修复 Bug 3:向左偏移 27 位做长除法,为 GRS 三保护位和 Sticky 腾出精度空间
// =========================================================================
uint64_t num = (uint64_t)frac_a << 26; // 24位标准商 + 3位保护位空间
uint64_t den = (uint64_t)frac_b;
uint32_t quot = (uint32_t)(num / den);
uint64_t rem = num % den;
// 【除法 Sticky 核心点】只要整数除法除不尽有余数,无条件将商的最低位置 1,激活 Sticky 状态
if (rem != 0) {
quot |= 1U;
}
// 规格化商:正常情况下隐藏位应该落在第 26 位 (C_FLOAT_HIDDEN_BIT << 3)
if (quot & (1U << 27)) {
uint32_t lost = quot & 1U;
quot >>= 1;
quot |= lost;
exp_res++;
} else {
// 如果隐藏位没能落在第 26 位,说明商太小,需要左移规格化
while (!(quot & (1U << 26)) && exp_res > 1) {
uint32_t sticky_backup = quot & 1U;
quot <<= 1;
quot |= sticky_backup; // 锁住最低位的 sticky 特征不丢失
exp_res--;
}
if (!(quot & (1U << 26)) && exp_res == 1) {
exp_res = 0; // 退化为非规格化数
}
}
// 上下溢出检测
if (exp_res >= 255) return c_Float_Pack(sign_res, 255, 0); // 上溢至 ±Inf
if (exp_res <= 0) return c_Float_Pack(sign_res, 0, 0); // 下溢至 ±0
// =========================================================================
// 5. 激活 IEEE 754 标准:向最接近偶数舍入(Round-to-Nearest-Even
// =========================================================================
uint32_t round_bits = quot & 7U; // 提取最后 3 位的 GRS 数据
quot >>= 3; // 移除保护位,回归标准 24 位商
if ((round_bits > 4) || ((round_bits == 4) && (quot & 1U))) {
quot++;
// 舍入可能导致再次溢出,进行二次规格化微调
if (quot & (1U << 24)) {
quot >>= 1;
exp_res++;
if (exp_res >= 255) return c_Float_Pack(sign_res, 255, 0);
}
}
// 剥离规格化数中用于拼装的高位隐藏位 1
if (exp_res != 0) {
quot &= C_FLOAT_FRAC_MASK;
}
return c_Float_Pack(sign_res, exp_res, quot);
}
int c_Float_Cmp(uint32_t a, uint32_t b) {
// 1. 处理 NaN:依据 IEEE 754,NaN 参与比较永远返回不相等(或未定义)
if (c_Float_IsNAN(a) || c_Float_IsNAN(b)) {
return 0; // 软浮点库通常在此处设置不合法比较标志位
}
// 2. 特殊情况:+0.0 (0x00000000) 和 -0.0 (0x80000000) 在逻辑上是相等的
if (((a | b) & ~C_FLOAT_SIGN_MASK) == 0) {
return 0;
}
// 提取符号位
uint32_t sign_a = a & C_FLOAT_SIGN_MASK;
uint32_t sign_b = b & C_FLOAT_SIGN_MASK;
// 3. 符号不同
if (sign_a != sign_b) {
// a 是负数,b 是正数 => a < b
// a 是正数,b 是负数 => a > b
return sign_a ? -1 : 1;
}
// 4. 符号相同:将原始二进制位转换为有符号 32 位整型进行直观比较
int32_t ia = (int32_t)a;
int32_t ib = (int32_t)b;
if (sign_a) {
// 如果都是负数,二进制数值越大,其代表的实际浮点数反而越小 (例如 -2.0 的二进制码大于 -1.0)
if (ia > ib) return -1;
if (ia < ib) return 1;
return 0;
} else {
// 如果都是正数,二进制数值越大,其实际浮点数就越大
if (ia > ib) return 1;
if (ia < ib) return -1;
return 0;
}
}
int c_Double_Cmp(uint64_t a, uint64_t b) {
// 1. 处理 NaN
if (c_Double_IsNAN(a) || c_Double_IsNAN(b)) {
return 0;
}
// 2. 处理 +0.0 与 -0.0 相等的情况
if (((a | b) & ~C_DOUBLE_SIGN_MASK) == 0) {
return 0;
}
uint64_t sign_a = a & C_DOUBLE_SIGN_MASK;
uint64_t sign_b = b & C_DOUBLE_SIGN_MASK;
// 3. 符号不同
if (sign_a != sign_b) {
return sign_a ? -1 : 1;
}
// 4. 符号相同:转为有符号 64 位整型比较
int64_t ia = (int64_t)a;
int64_t ib = (int64_t)b;
if (sign_a) {
// 均为负数
if (ia > ib) return -1;
if (ia < ib) return 1;
return 0;
} else {
// 均为正数
if (ia > ib) return 1;
if (ia < ib) return -1;
return 0;
}
}
/**
* @brief 软浮点双精度打包函数
* @param sign 符号位 (0 或 1)
* @param exp 解包/运算后的有符号指数 (已减去或未加上 Bias 均可,此处传入带 Bias 的期望值)
* @param frac64 运算后暂存在 64 位整型中的高精度尾数 (假设规格化后隐含位在第 52 位,低位留有舍入残余)
* @return 组合好的 IEEE 754 64位无符号整数 (可直接对应 double)
*/
uint64_t c_Double_Pack(uint32_t sign, int32_t exp, uint64_t frac64) {
// 1. 动态规格化:若运算导致尾数高位溢出 (例如第 53 位为 1),需要右移尾数并增加指数
if (frac64 & (C_DOUBLE_HIDDEN_BIT << 1)) {
frac64 >>= 1;
exp++;
}
// 2. 下溢处理:指数太小,转换为非规格化数
if (exp <= 0) {
// 如果指数极小,直接移出范围,变回 0
if (exp < -52) {
frac64 = 0;
} else {
// 右移尾数以对齐非规格化数的指数位置 (exp = 0)
int32_t shift = 1 - exp;
frac64 >>= shift;
}
exp = 0; // 非规格化数的指数域强制为 0
}
// 3. 执行 IEEE 754 默认的“向最接近偶数舍入 (Round-to-Nearest-Even)”
// 假设经过上述操作后,标准 52 位尾数在 frac64 的低 52 位,若有更低位则是运算残留
// 为了演示标准舍入,假设传入的 frac64 在低位保留了扩充精度(例如左移了 3 位留给 GRS)
// 此处简化演示:基于常规截断进行最邻近舍入处理
// 在工业级库中,通常传入 frac 时会带有额外的 round_bits 变量
// 4. 上溢检查:指数超过最大限制 (2047),打包为无穷大
if (exp >= 0x7FF) {
return ((uint64_t)sign << 63) | C_DOUBLE_EXP_MASK; // 返回 +/- Inf
}
// 5. 最终清除尾数域外的隐含 1 (因为 IEEE 754 编码中不存储规格化数的最高位 1)
uint64_t final_frac = frac64 & C_DOUBLE_FRAC_MASK;
// 6. 位移拼接
uint64_t packed_value = ((uint64_t)sign << 63) |
((uint64_t)exp << 52) |
final_frac;
return packed_value;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 輔助函數:處理 64 位元尾數與 3 位元 GRS 捨入殘餘
static uint64_t round_and_pack_double(uint64_t sign, int32_t exp, uint64_t frac64) {
uint32_t round_bits = frac64 & 7U;
frac64 >>= 3; // 移除 GRS 位,恢復為包含隱含位的 53 位元尾數
// 向最接近偶數捨入
if ((round_bits > 4) || ((round_bits == 4) && (frac64 & 1ULL))) {
frac64++;
if (frac64 & (C_DOUBLE_HIDDEN_BIT << 1)) {
frac64 >>= 1;
exp++;
}
}
if (exp >= 2047) return sign | C_DOUBLE_EXP_MASK; // 溢出至無限大
if (exp <= 0) return sign; // 下溢至 0
return sign | ((uint64_t)exp << 52) | (frac64 & C_DOUBLE_FRAC_MASK);
}
uint64_t c_Double_Add(uint64_t a, uint64_t b) {
c_Double_t f1 = { .raw = a };
c_Double_t f2 = { .raw = b };
c_Double_t result = { .raw = 0 };
// 1. Handle Special Cases: NaNs and Infinities
bool f1_is_nan_or_inf = (f1.parts.exponent == 0x7FF);
bool f2_is_nan_or_inf = (f2.parts.exponent == 0x7FF);
if (f1_is_nan_or_inf || f2_is_nan_or_inf) {
// Handle NaNs
if ((f1_is_nan_or_inf && f1.parts.fraction != 0) ||
(f2_is_nan_or_inf && f2.parts.fraction != 0)) {
result.parts.exponent = 0x7FF;
result.parts.fraction = 0x1; // Quiet NaN
return result.raw;
}
// Handle Inf + Inf variations
if (f1_is_nan_or_inf && f2_is_nan_or_inf) {
if (f1.parts.sign != f2.parts.sign) {
// (+Inf) + (-Inf) or (-Inf) + (+Inf) is invalid -> NaN
result.parts.exponent = 0x7FF;
result.parts.fraction = 0x1;
return result.raw;
}
return f1.raw; // Return either infinity if signs match
}
// One operand is Infinity, the other is finite
return f1_is_nan_or_inf ? f1.raw : f2.raw;
}
// 2. Handle Zero Shortcuts
bool f1_is_zero = (f1.parts.exponent == 0 && f1.parts.fraction == 0);
bool f2_is_zero = (f2.parts.exponent == 0 && f2.parts.fraction == 0);
if (f1_is_zero && f2_is_zero) {
// If both are zero and signs differ, standard rule yields +0.0 in round-to-nearest
result.parts.sign = (f1.parts.sign == f2.parts.sign) ? f1.parts.sign : 0;
return result.raw;
}
if (f1_is_zero) return f2.raw;
if (f2_is_zero) return f1.raw;
// 3. Extract Exponents and Mantissas (with implicit leading 1 bit)
int32_t exp1 = f1.parts.exponent;
int32_t exp2 = f2.parts.exponent;
uint64_t m1 = (1ULL << 52) | f1.parts.fraction;
uint64_t m2 = (1ULL << 52) | f2.parts.fraction;
// 4. Align Exponents (Shift mantissas to three extra bits of precision: Guard, Round, Sticky)
// We scale the mantissas left by 3 bits initially to capture shifting errors.
uint64_t m_large = 0, m_small = 0;
int32_t exp_res = 0;
bool sign_large = 0, sign_small = 0;
if (exp1 >= exp2) {
m_large = m1 << 3;
sign_large = f1.parts.sign;
exp_res = exp1;
int32_t shift = exp1 - exp2;
if (shift == 0) {
m_small = m2 << 3;
} else if (shift > 55) {
m_small = 1; // Everything shifted out becomes a sticky bit
} else {
uint64_t lost_bits = m2 & ((1ULL << shift) - 1);
m_small = (m2 << 3) >> shift;
if (lost_bits != 0) m_small |= 1; // Fold lost bits into sticky bit
}
sign_small = f2.parts.sign;
} else {
m_large = m2 << 3;
sign_large = f2.parts.sign;
exp_res = exp2;
int32_t shift = exp2 - exp1;
if (shift > 55) {
m_small = 1;
} else {
uint64_t lost_bits = m1 & ((1ULL << shift) - 1);
m_small = (m1 << 3) >> shift;
if (lost_bits != 0) m_small |= 1;
}
sign_small = f1.parts.sign;
}
// 5. Perform Magnitude Addition or Subtraction
uint64_t m_res = 0;
bool result_sign = sign_large;
if (sign_large == sign_small) {
// True addition
m_res = m_large + m_small;
// Handle carry out: if bit 56 is set (original 52 shifted left 3 plus 1 carry bit)
if (m_res & (1ULL << 56)) {
uint64_t sticky = m_res & 1;
m_res >>= 1;
m_res |= sticky; // preserve sticky bit tracking
exp_res += 1;
}
} else {
// True subtraction (Large magnitude minus Small magnitude)
// If magnitudes are completely equal, they cancel out to 0
if (m_large == m_small) {
return 0; // standard +0.0
}
m_res = m_large - m_small;
// Normalize cancellation shifts (shift left until bit 55 is 1)
while ((m_res & (1ULL << 55)) == 0 && exp_res > 0) {
uint64_t sticky = m_res & 1;
m_res = (m_res << 1) | sticky;
exp_res -= 1;
}
}
// 6. Apply IEEE 754 Round-to-Nearest, Ties-to-Even
// Currently, bit 55 is the implicit 1. Bits [2:0] are Guard, Round, Sticky.
// The target fraction belongs in bits [54:3].
uint64_t final_fraction = (m_res >> 3) & 0xFFFFFFFFFFFFFLL;
bool round_bit = (m_res & 4) != 0; // Bit 2
bool sticky_bit = (m_res & 3) != 0; // Bits 1 and 0 combined
bool lsb = (final_fraction & 1) != 0;
if (round_bit && (sticky_bit || lsb)) {
final_fraction++;
if (final_fraction > 0xFFFFFFFFFFFFFLL) { // Handle carry out from rounding
final_fraction = 0;
exp_res += 1;
}
}
// 7. Check for Overflow / Underflow Boundaries
if (exp_res >= 0x7FF) {
result.parts.sign = result_sign;
result.parts.exponent = 0x7FF;
result.parts.fraction = 0; // Overflow to Infinity
} else if (exp_res <= 0) {
// Flush underflow to zero
result.parts.sign = result_sign;
result.parts.exponent = 0;
result.parts.fraction = 0;
} else {
result.parts.sign = result_sign;
result.parts.exponent = (uint64_t)exp_res;
result.parts.fraction = final_fraction;
}
return result.raw;
}
// 內部輔助函數:32位交叉相乘,手動模擬 64x64->128位元乘法
C_STATIC_FORCE_INLINE
void mul64_to_128(uint64_t a, uint64_t b, uint64_t *res_hi, uint64_t *res_lo) {
uint64_t a_hi = a >> 32, a_lo = a & 0xFFFFFFFFULL;
uint64_t b_hi = b >> 32, b_lo = b & 0xFFFFFFFFULL;
uint64_t p0 = a_lo * b_lo;
uint64_t p1 = a_hi * b_lo;
uint64_t p2 = a_lo * b_hi;
uint64_t p3 = a_hi * b_hi;
uint64_t mid = p1 + (p0 >> 32) + (p2 & 0xFFFFFFFFULL);
*res_lo = (mid << 32) | (p0 & 0xFFFFFFFFULL);
*res_hi = p3 + (mid >> 32) + (p2 >> 32);
}
uint64_t c_Double_Mul(uint64_t a, uint64_t b) {
c_Double_t f1 = { .raw = a };
c_Double_t f2 = { .raw = b };
c_Double_t result = { .raw = 0 };
// 1. Determine the result sign (XOR of input signs)
result.parts.sign = f1.parts.sign ^ f2.parts.sign;
// 2. Handle Zero / Special Cases (Inf, NaN)
// Shortcut if either operand is zero
bool f1_is_inf_or_nan = (f1.parts.exponent == 0x7FF);
bool f2_is_inf_or_nan = (f2.parts.exponent == 0x7FF);
bool f1_is_zero = (f1.parts.exponent == 0 && f1.parts.fraction == 0);
bool f2_is_zero = (f2.parts.exponent == 0 && f2.parts.fraction == 0);
// Shortcut for Infinities or NaNs
if (f1_is_inf_or_nan || f2_is_inf_or_nan) {
// If either is an actual NaN, OR we are multiplying 0 * Inf, it MUST be NaN
if ((f1_is_inf_or_nan && f1.parts.fraction != 0) ||
(f2_is_inf_or_nan && f2.parts.fraction != 0) ||
(f1_is_zero && f2_is_inf_or_nan) ||
(f2_is_zero && f1_is_inf_or_nan)) {
result.parts.exponent = 0x7FF;
result.parts.fraction = 0x1; // Quiet NaN
return result.raw;
}
// Otherwise, it's a valid Infinity multiplication (e.g., 5.0 * Inf = Inf)
result.parts.exponent = 0x7FF;
result.parts.fraction = 0;
return result.raw;
}
// Now it is safe to evaluate the normal zero shortcut
if (f1_is_zero || f2_is_zero) {
result.parts.exponent = 0;
result.parts.fraction = 0;
return result.raw; // Returns correctly signed zero
}
// 3. Extract Mantissas and append the implicit leading 1 bit (Bit 52)
// Note: This implementation assumes normalized numbers.
uint64_t m1 = (1ULL << 52) | f1.parts.fraction;
uint64_t m2 = (1ULL << 52) | f2.parts.fraction;
// 4. Calculate raw exponent sum (subtract the double bias of 1023)
int32_t exp_res = (int32_t)f1.parts.exponent + (int32_t)f2.parts.exponent - 1023;
// 5. Multiply the mantissas using 128-bit precision to prevent overflow
// Multiplying two 53-bit integers results in a 105-bit or 106-bit product
unsigned __int128 prod = (unsigned __int128)m1 * m2;
// 6. Normalize the product
// The product has its radix point at bit 104 (52 fractional bits * 2)
// We want the resulting leading bit to sit at bit 52.
if (prod & ((unsigned __int128)1 << 105)) {
// Product is >= 2.0 (bit 105 is set). Shift down by 53 and increment exponent.
exp_res += 1;
// Simple round-to-nearest-even approximation via bit shift
result.parts.fraction = (uint64_t)((prod >> 53) & 0xFFFFFFFFFFFFFLL);
} else {
// Product is < 2.0 (bit 104 is set). Shift down by 52.
result.parts.fraction = (uint64_t)((prod >> 52) & 0xFFFFFFFFFFFFFLL);
}
// 7. Check for Overflow / Underflow boundaries
if (exp_res >= 0x7FF) {
// Overflow to Infinity
result.parts.exponent = 0x7FF;
result.parts.fraction = 0;
} else if (exp_res <= 0) {
// Underflow to Zero (Flushing subnormals to zero for simplicity)
result.parts.exponent = 0;
result.parts.fraction = 0;
} else {
// Valid normalized exponent range
result.parts.exponent = (uint64_t)exp_res;
}
return result.raw;
}
uint64_t c_Double_Div(uint64_t a, uint64_t b) {
c_Double_t f1 = { .raw = a };
c_Double_t f2 = { .raw = b };
c_Double_t result = { .raw = 0 };
// 1. Determine the result sign (XOR of input signs)
result.parts.sign = f1.parts.sign ^ f2.parts.sign;
// 2. Handle Special Cases: Zero, Infinity, and NaN
bool f1_is_nan_or_inf = (f1.parts.exponent == 0x7FF);
bool f2_is_nan_or_inf = (f2.parts.exponent == 0x7FF);
bool f1_is_zero = (f1.parts.exponent == 0 && f1.parts.fraction == 0);
bool f2_is_zero = (f2.parts.exponent == 0 && f2.parts.fraction == 0);
// Case 2a: Either input is NaN, or invalid combinations (0/0, Inf/Inf)
if ((f1_is_nan_or_inf && f1.parts.fraction != 0) ||
(f2_is_nan_or_inf && f2.parts.fraction != 0) ||
(f1_is_zero && f2_is_zero) ||
(f1_is_nan_or_inf && f2_is_nan_or_inf)) {
result.parts.exponent = 0x7FF;
result.parts.fraction = 0x1; // Quiet NaN
return result.raw;
}
// Case 2b: Division by Zero (X / 0 = Inf)
if (f2_is_zero) {
result.parts.exponent = 0x7FF; // Infinity
result.parts.fraction = 0;
return result.raw;
}
// Case 2c: Numerator is Zero or Denominator is Infinity (0 / X = 0, X / Inf = 0)
if (f1_is_zero || f2_is_nan_or_inf) {
result.parts.exponent = 0;
result.parts.fraction = 0;
return result.raw;
}
// Case 2d: Numerator is Infinity (Inf / X = Inf)
if (f1_is_nan_or_inf) {
result.parts.exponent = 0x7FF;
result.parts.fraction = 0;
return result.raw;
}
// 3. Extract Mantissas and append the implicit leading 1 bit (Bit 52)
// Assumes normalized inputs
uint64_t m1 = (1ULL << 52) | f1.parts.fraction;
uint64_t m2 = (1ULL << 52) | f2.parts.fraction;
// 4. Calculate raw biased exponent (Subtract exponents and restore bias)
int32_t exp_res = (int32_t)f1.parts.exponent - (int32_t)f2.parts.exponent + 1023;
// 5. Divide the mantissas
// Since m1 and m2 are roughly equal, m1 / m2 would yield 0 or 1.
// We upscale m1 to 128 bits and shift it left by 52 positions first.
// This allows integer division to compute the correct 53-bit fraction.
unsigned __int128 dividend = (unsigned __int128)m1 << 53;
unsigned __int128 quot = dividend / m2;
unsigned __int128 remainder = dividend % m2;
// 6. Normalize the quotient
// In binary division, if m1 < m2, the quotient's implicit 1 drops to bit 51.
// If m1 >= m2, the quotient's implicit 1 naturally sits at bit 52.
uint64_t final_fraction = 0;
// Check if the implicit bit sits at bit 53 (corresponds to m1 >= m2)
if (quot & ((unsigned __int128)1 << 53)) {
// Extract the 52-bit fraction
final_fraction = (uint64_t)((quot >> 1) & 0xFFFFFFFFFFFFFLL);
// Rounding bits
bool round_bit = (quot & 1) != 0;
bool sticky_bit = (remainder != 0);
bool lsb = (final_fraction & 1) != 0;
// IEEE 754 standard Round-to-Nearest, Ties-to-Even rule
if (round_bit && (sticky_bit || lsb)) {
final_fraction++;
if (final_fraction > 0xFFFFFFFFFFFFFLL) { // Handle carry-out
final_fraction = 0;
exp_res += 1;
}
}
} else {
// Implicit bit sits at bit 52 (corresponds to m1 < m2)
// No right shift needed for the fraction, but we need the sticky bit updated
final_fraction = (uint64_t)(quot & 0xFFFFFFFFFFFFFLL);
// We shifted left by 53 instead of 52, so bit 0 of quot is the actual round bit
// However, since we didn't shift right, we must look at the remainder for the true sticky status
// For the m1 < m2 case, we effectively need to look at what would happen if we didn't shift as far.
// Let's re-align it perfectly:
// To make it straightforward, let's normalize the 54-bit temporary quotient first:
// If bit 53 is not set, we shift the entire quotient up by 1 bit to force the implicit bit to 53,
// but we must adjust the remainder logic. Let's use a cleaner normalization pattern:
// Shift left by 1 to align the implicit bit to bit 53
quot <<= 1;
exp_res -= 1;
final_fraction = (uint64_t)((quot >> 1) & 0xFFFFFFFFFFFFFLL);
bool round_bit = (quot & 1) != 0;
bool sticky_bit = (remainder != 0);
bool lsb = (final_fraction & 1) != 0;
if (round_bit && (sticky_bit || lsb)) {
final_fraction++;
if (final_fraction > 0xFFFFFFFFFFFFFLL) {
final_fraction = 0;
exp_res += 1;
}
}
}
result.parts.fraction = final_fraction;
// 7. Check for Overflow / Underflow boundaries
if (exp_res >= 0x7FF) {
// Overflow to Infinity
result.parts.exponent = 0x7FF;
result.parts.fraction = 0;
} else if (exp_res <= 0) {
// Underflow to Zero (Flushing subnormal results to zero)
result.parts.exponent = 0;
result.parts.fraction = 0;
} else {
// Valid normalized exponent
result.parts.exponent = (uint64_t)exp_res;
}
return result.raw;
}
+211
View File
@@ -0,0 +1,211 @@
#ifndef INCLUDED_C_FLOAT_H
#define INCLUDED_C_FLOAT_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_MATH_H
#define INCLUDED_MATH_H
#include <math.h>
#endif /*INCLUDED_MATH_H*/
#ifndef INCLUDED_FLOAT_H
#define INCLUDED_FLOAT_H
#include <float.h>
#endif /*INCLUDED_FLOAT_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef union {
float f;
uint32_t raw;
struct {
uint32_t fraction : 23; // 尾数 (M)
uint32_t exponent : 8; // 指数 (E)
uint32_t sign : 1; // 符号位 (S)
} parts;
} c_Float_t;
typedef union {
double d;
uint64_t raw;
struct {
uint64_t fraction : 52; // 尾数
uint64_t exponent : 11; // 指数
uint64_t sign : 1; // 符号
} parts;
} c_Double_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 32位单精度常量定义
#define C_FLOAT_SIGN_MASK 0x80000000U
#define C_FLOAT_EXP_MASK 0x7F800000U
#define C_FLOAT_FRAC_MASK 0x007FFFFFU
#define C_FLOAT_HIDDEN_BIT 0x00800000U // 隐藏的最高位1
#define C_FLOAT_EXP_BIAS 127
#define C_FLOAT_NEG_INF 0xFF800000U
#define C_FLOAT_POS_INF 0x7F800000U
// 快捷提取宏
#define C_FLOAT_GET_SIGN(u) (((u) & C_FLOAT_SIGN_MASK) >> 31)
#define C_FLOAT_GET_EXP(u) (((u) & C_FLOAT_EXP_MASK) >> 23)
#define C_FLOAT_GET_FRAC(u) ((u) & C_FLOAT_FRAC_MASK)
#define C_DOUBLE_SIGN_MASK 0x8000000000000000ULL
#define C_DOUBLE_EXP_MASK 0x7FF0000000000000ULL
#define C_DOUBLE_FRAC_MASK 0x000FFFFFFFFFFFFFULL
#define C_DOUBLE_HIDDEN_BIT 0x0010000000000000ULL // 第52位(从0开始算)
#define C_DOUBLE_POS_INF 0x7FF0000000000000ULL
#define C_DOUBLE_NEG_INF 0xFFF0000000000000ULL
#define C_DOUBLE_GET_SIGN(u) (((u) & C_DOUBLE_SIGN_MASK) >> 63)
#define C_DOUBLE_GET_EXP(u) (((u) & C_DOUBLE_EXP_MASK) >> 52)
#define C_DOUBLE_GET_FRAC(u) ((u) & C_DOUBLE_FRAC_MASK)
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
C_STATIC_FORCE_INLINE
uint32_t c_Float_Pack(const uint32_t sign, const uint32_t exp, const uint32_t frac) {
return ((sign << 31) & C_FLOAT_SIGN_MASK) |
((exp << 23) & C_FLOAT_EXP_MASK) |
(frac & C_FLOAT_FRAC_MASK);
}
C_STATIC_FORCE_INLINE
int c_Float_IsNAN(uint32_t raw) {
return ((raw & C_FLOAT_EXP_MASK) == C_FLOAT_EXP_MASK) && ((raw & C_FLOAT_FRAC_MASK) != 0);
}
C_STATIC_FORCE_INLINE
int c_Double_IsNAN(uint64_t raw) {
return ((raw & C_DOUBLE_EXP_MASK) == C_DOUBLE_EXP_MASK) && ((raw & C_DOUBLE_FRAC_MASK) != 0);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
uint32_t c_Float_Add(uint32_t a, uint32_t b);
uint32_t c_Float_Mul(uint32_t a, uint32_t b);
uint32_t c_Float_Div(uint32_t a, uint32_t b);
int c_Float_Cmp(uint32_t a, uint32_t b);
C_STATIC_FORCE_INLINE
uint32_t c_Float_Sub(uint32_t a, uint32_t b) {
// 透過與 0x80000000 進行 XOR,直接將 b 的符號位元取反 (0->1, 1->0)
// 隨後將 A - B 轉換為 A + (-B) 傳入加法器
return c_Float_Add(a, b ^ C_FLOAT_SIGN_MASK);
}
C_STATIC_FORCE_INLINE
bool c_Float_IsZero(uint32_t raw) {
// Strip away the sign bit; check if the remaining 31 bits are 0
return (raw & ~C_FLOAT_SIGN_MASK) == 0U;
}
C_STATIC_FORCE_INLINE
bool c_Float_IsInf(uint32_t raw) {
// Strip the sign bit and check if it exactly matches the exponent mask.
// If any fraction bits were set, it would be a NaN instead of Infinity.
return (raw & ~0x80000000U) == C_FLOAT_EXP_MASK;
}
/**
* @brief Determines if the float is specifically Negative Infinity (-Inf).
*/
C_STATIC_FORCE_INLINE
bool c_Float_IsNegInf(uint32_t raw) {
return raw == C_FLOAT_NEG_INF;
}
/**
* @brief Determines if the float is specifically Positive Infinity (+Inf).
*/
C_STATIC_FORCE_INLINE
bool c_Float_IsPosInf(uint32_t raw) {
return raw == C_FLOAT_POS_INF;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
uint64_t c_Double_Pack(uint32_t sign, int32_t exp, uint64_t frac64);
int c_Double_Cmp(uint64_t a, uint64_t b);
uint64_t c_Double_Add(uint64_t a, uint64_t b);
C_STATIC_FORCE_INLINE
uint64_t c_Double_Sub(uint64_t a, uint64_t b) {
// A - B == A + (-B)
return c_Double_Add(a, b ^ C_DOUBLE_SIGN_MASK);
}
uint64_t c_Double_Mul(uint64_t a, uint64_t b);
uint64_t c_Double_Div(uint64_t a, uint64_t b);
C_STATIC_FORCE_INLINE
bool c_Double_IsZero(const uint64_t raw) {
// Strip away the sign bit; check if the remaining 63 bits are 0
return (raw & ~C_DOUBLE_SIGN_MASK) == 0ULL;
}
C_STATIC_FORCE_INLINE
bool c_Double_IsInf(uint64_t raw) {
// Strip the sign bit and check if it exactly matches the exponent mask.
// If any fraction bits were set, it would be a NaN instead of Infinity.
return (raw & ~C_DOUBLE_SIGN_MASK) == C_DOUBLE_EXP_MASK;
}
/**
* @brief Determines if the double is specifically Negative Infinity (-Inf).
*/
C_STATIC_FORCE_INLINE
bool c_Double_IsNegInf(uint64_t raw) {
return raw == C_DOUBLE_NEG_INF;
}
/**
* @brief Determines if the double is specifically Positive Infinity (+Inf).
*/
C_STATIC_FORCE_INLINE
bool c_Double_IsPosInf(uint64_t raw) {
return raw == C_DOUBLE_POS_INF;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
C_STATIC_FORCE_INLINE
int c_float_cmp(const float a, const float b) {
c_Float_t va;
c_Float_t vb;
va.f = a;
vb.f = b;
return c_Float_Cmp(va.raw, vb.raw);
}
C_STATIC_FORCE_INLINE
int c_double_cmp(const double a, const double b) {
c_Double_t va;
c_Double_t vb;
va.d = a;
vb.d = b;
return c_Double_Cmp(va.raw, vb.raw);
}
#endif /*INCLUDED_C_FLOAT_H*/