有没有比收敛泰勒级数更有效的方法来分治 64 位硬件上的 uint 256 日志? 首先将整数转换为二进制浮点数然后是尾数上的多项式近似

问题描述

我希望将 256 位无符号整数的对数基数 n(10 可以)作为 Rust 中的浮点,而不会损失精度。在我看来,我需要实现一个 8xf64 512 位浮点 512 类型并使用泰勒级数来近似 ln 然后是对数。我知道有一些汇编方法可以获取 f64 的日志。我想知道堆栈溢出是否有人能想到分而治之或其他更有效的方法。我愿意在 8xf64 512 位数组上运行内联汇编。

解决方法

这可能是一个有用的算法起点/大纲。 IDK 是否会给您精确 结果,例如错误 bc / dc / calc 这样的扩展精度计算器的作用。

我认为 log 收敛很快,因此如果您要进行牛顿迭代来优化,这种位扫描方法可能是获得良好起点的快速方法。即使你真的只需要大约 256 位正确的尾数位,我也不知道需要多大的多项式才能得到它,并且每个乘法/加法/fma 将在 512 位(8x)或 320 位(5x)上双精度)。


首先将整数转换为二进制浮点数

对于正常大小的浮点数,通常的方法利用二进制浮点数的对数性质。如果没有 256 位硬件浮点数,您需要自己找到 ilog2(int),即最高设置位 (Efficiently find least significant set bit in a large array?) 的位置。

然后将您的 256 位整数视为 [1..2) 或 [0.5 .. 1) 范围内数字的尾数,是的,对 log2() 使用多项式近似值,在此范围内是准确的范围有限。 (在实际的软浮点数之前,您可能希望将数字左移以使其标准化,即最高设置位位于顶部。即 x <<= clz(x)


然后是尾数上的多项式近似

然后加上整数指数 + log_approx(mantissa) => log2(x).

Efficient implementation of log2(__m256d) in AVX2 有更多关于实现 log2(double) 的详细信息(SIMD 一次执行 4 次,与执行一次扩展精度计算非常不同)。

它包括一些实现的链接,例如Agner Fog 的 VCL 使用两个多项式的比率而不是一个更大的多项式,并使用各种技巧来保持尽可能高的精度:https://github.com/vectorclass/version2/blob/9874e4bfc7a0919fda16596144d393da5f8bf6c0/vectormath_exp.h#L942。如进一步缩小范围:如果x > SQRT2*0.5,则增加指数并将尾数加倍。 (如果 512 位 FP 除法真的很昂贵,您可能只需在一个多项式中使用更多项。)VCL 目前已获得 Apache 许可,因此您可以随意将其复制到任何内容中。

IDK 如果有更多技巧可能对大扩展精度或软浮点变得更有价值,而该实现使用。 VCL 的数学函数比一些更快的近似值花费更多的精力来保持高精度,但它们并不精确。


你真的需要 512 位浮点数吗?也许只有 320 位(5 倍双)?

如果您不需要比 double 更大的指数范围,您可以将 技术扩展到更宽的浮点数,利用硬件 FP 获得 52 或 53 个尾数位每个 64 位块。 (根据评论,显然您已经打算这样做。)

您可能不需要 512 位浮点数即可获得足够的精度。 256/52 = 4.92,因此只有 5x double 块比您的输入具有更高的精度(尾数位),并且可以准确表示任何 256 位整数。 (IEEE double 确实有足够大的指数范围;-1022 .. +1023)。并且有足够的余力让 log2(int) 将每个 256 位输入映射到唯一的单调输出,即使存在一些舍入误差。