【发布时间】:2021-11-12 06:28:30
【问题描述】:
我很好奇将浮点值 x 包装在半封闭区间 [0; a[ 中的方法。
例如,我可以有一个任意实数,比如x = 354638.515,我希望将它折叠成[0; 2π[,因为我有一个很好的sin 近似值。
fmod 标准 C 函数在我的基准测试中显示得相当高,通过检查各种 libc 实现的源代码,我可以理解原因:这东西相当分支,可能是为了处理很多IEEE754 特定问题:
- glibc:https://github.com/bminor/glibc/blob/master/sysdeps/ieee754/flt-32/e_fmodf.c
- 苹果:https://opensource.apple.com/source/Libm/Libm-315/Source/ARM/fmod.c.auto.html
- musl:https://git.musl-libc.org/cgit/musl/tree/src/math/fmod.c
- 使用
-ffast-math运行会导致 GCC 生成通过 x86/x86_64 上的 x87 FPU 的代码,这会带来一系列问题(例如 80 位双精度、FP 状态和其他有趣的事情)。我希望实现至少半正确地进行矢量化,并且如果可能的话,至少不要通过 x87 FPU,而是通过矢量寄存器,因为我的其余代码最终会被矢量化,即使不一定是最佳方式,由编译器. - 这个看起来简单多了:https://github.com/KnightOS/libc/blob/master/src/fmod.c
就我而言,我只关心通常的实数值,而不是 NaN,也不关心无穷大。我的范围在编译时也是已知的并且是正常的(常见的情况是 π/2),因此不需要检查诸如 range == 0 之类的“特殊情况”。
因此,对于该特定用例,fmod 的良好实现是什么?
【问题讨论】:
-
你能拿到标准
fmod的源代码,并去掉所有的安全检查吗? -
“表现得相当高”不是一个有意义的陈述。高性能?高时间?成本高?效率高吗?
-
参数缩减的标准技术是 Payne-Hanek 方法或它的一些子集或变体,具体取决于您的特定需求。一个乘以 1/(2π) 的准备值并取余数(通常通过将商截断为整数并使用 FMA 推导出余数)。可以不使用 FMA 获取残差,而是获取产品的分数部分,这与残差的不同之处在于它已按 1/(2π) 缩放,但随后您将应用为此设计的
sin近似值。 -
也就是说,1/(2π) 的使用取决于需要。对于简单的近似值,单个
double值可能就足够了。为了获得更好的准确性,您可能需要某种形式的扩展精度。要覆盖完整的浮点范围,您可能需要一个值表,该表按要减少的值的指数进行索引。然后,当减少的数字如此之大以至于这些位乘以 1/(2π) 中的高位总是整数时,可以省略高位。因此,在一个 Stack Overflow 答案中要涵盖的内容和记录的内容太多。这取决于你的情况。 -
除非询问两种语言之间的差异或交互,否则不要同时标记 C 和 C++。选择一个并删除另一个标签。
标签: c++ math floating-point vectorization