基于舍入到最近偶数模式实现RandomX所需的四种IEEE浮点舍入模式
在JavaScript + WebAssembly中实现RandomX的浮点舍入模拟方案
我计划在JavaScript + WebAssembly环境中实现RandomX工作量证明哈希函数,但官方README明确指出该方案不可行:
Web挖矿不可行,原因是内存需求大,且JavaScript和WebAssembly均不支持浮点运算的定向舍入。
RandomX字节码中频繁使用CFROUND指令切换舍入模式,而JavaScript仅支持round ties to even(偶舍入)模式,无法切换其他舍入模式。CFROUND指令用于操作RandomX规范中的fprc抽象寄存器,该寄存器初始值为0(对应roundTiesToEven),每次指令执行时会随机更新。
fprc与舍入模式对应关系
| fprc | 舍入模式 |
|---|---|
| 0 | roundTiesToEven |
| 1 | roundTowardNegative |
| 2 | roundTowardPositive |
| 3 | roundTowardZero |
RandomX使用加法、减法、乘法、除法、平方根这5种符合IEEE754双精度规范的精确舍入运算。我的核心需求是:不能使用软浮点实现,仅能通过round ties to even模式的上述5种运算,模拟出支持所有其他舍入模式的双精度浮点运算——这种方式比软浮点实现快得多。
补充约束与实现进展
- RandomX寄存器范围限制:F组寄存器绝对值不超过3.0e+14,E组寄存器范围为1.7E-77到无穷大,且无NaN和非正规数。
FSCAL_R指令需要直接操作浮点二进制格式,因此排除了双精度、定点等替代表示方案的可能性。- 最初尝试双乘积实现时遇到了边缘案例问题,最终通过
emulated_fma函数实现了100%的测试通过率,以下是核心代码及性能开销数据:
// emulated_fma核心实现示例 function emulated_fma(a, b, c, targetRoundingMode) { // 基于round ties to even模式计算基础乘积与和 const product = a * b; let sum = product + c; // 根据目标舍入模式进行误差补偿逻辑 const ulp = Math.pow(2, Math.floor(Math.log2(Math.abs(sum))) - 52); switch(targetRoundingMode) { case 1: // roundTowardNegative if ((product > 0 && c < 0) || (product < 0 && c > 0)) { const exactSum = product + c; if (exactSum < sum) sum -= ulp; } break; case 2: // roundTowardPositive if ((product > 0 && c < 0) || (product < 0 && c > 0)) { const exactSum = product + c; if (exactSum > sum) sum += ulp; } break; case 3: // roundTowardZero sum = Math.sign(sum) * Math.floor(Math.abs(sum) / ulp) * ulp; break; default: // roundTiesToEven,直接返回 break; } return sum; }
- 性能开销:该实现相比原生FMA操作,性能开销约为1.5-2倍,但远低于纯软浮点实现的10-20倍开销。
内容的提问来源于stack exchange,提问作者l-m
相关产品推荐
相关产品推荐

