实施 std::nextafter:非正规为零模式是否会影响它?如果是这样,怎么做?

计算科学 C 浮点 计算机算术
2021-12-21 08:58:49

对于这个问题,这可能是错误的 stackexchange 站点。math.SE、cs.SE、programmers.SE,当然还有 stackoverflow 都是可能的。我希望能接触到可能实际使用此功能的受众,以获得有关他们希望它如何工作的一些反馈。

C 的double nextafter(double x, double y)函数从 x 沿 y 方向返回下一个可表示的值。(通常为 + 或 - 无穷大,但可以是任何值)。

我正在整理 GNU libc/libm 的实现以编译成更好的代码。(它目前将每个双精度数提取为两个 32 位整数。它非常笨重并且使用了很多分支,并且没有利用int64_t.)。如果我要改变它,我可能会尽可能多地改进,但使用 FP compare-equal 会改变 DAZ 模式下的结果。

当 denormals-are-zero 和/或 flush-to-zero 处于活动状态时,它应该如何表现? 在 x86 上,有两个单独的标志会牺牲逐渐下溢来提高性能。默认情况下,两者均未设置。可以设置一个或两个(它们经常一起使用,但不一定是):

  • DAZ 是一个输入过滤器:当 FP 数学/比较指令读取其输入时,非规范化被认为是 +/-0.0。因此,两个非正规之间的比较发现它们是相等的。不过,算术很容易产生反规范的结果。

  • FTZ 是一个输出过滤器:非正规输入正常工作,对比较指令没有影响。但是,普通的数学指令不能生成非规范化。异常结果刷新为 +/-0.0。 DBL_MIN/2 + DBL_MIN/1.5应该给出与没有 FTZ 相同的结果(例如,如果将这些输入非正规加载为常量)。

在这两种情况下(尤其是 DAZ),有人可能会争辩说,下一个可表示的值DBL_MIN(最小的正常数)是+0.0,而下一个数字+0.0实现这一点需要检查 FTZ/DAZ 标志。-0.0-DBL_MIN

有人可能会争辩说,在 DAZ 模式下,两个不同的非正规应该被视为相等,因为它们确实比较相等。因此,根据POSIXC11 标准(7.12.11.3 pg 256 和 Annex F.10.8.3 pg 529)的要求,yxequals时返回是正确的。(本规范的动机是返回,反之亦然。)ynextafter(0.0, -0.0)-0.0

第二种行为允许非常快速和紧凑的实现(没有采用的分支和 3 个未采用的分支污染分支预测器,并且英特尔 Haswell 上约 9 个时钟周期延迟(作为比较,FP 乘法需要 5 个周期)对于常见情况(x!=y, and x!=0, 结果不会上溢(infinity) 或下溢(denormal))。与glibc 的当前版本相比,不到总代码大小的一半。

C11 标准说:

“即使可能发生下溢或上溢,返回值与当前舍入方向模式无关。”

但 DAZ 不是舍入方向模式。

boost::math::ulp函数的文档观察到“我们的经验是,std::nextafter经常会根据哪些优化和硬件标志生效而中断”。


glibc 的功能需要与其他实现的nextafter一致性,但与 glibc 现有行为的一致性可能优先。当前实现完全不受任何 FP 设置的影响,因为它仅对 IEEE 浮点位模式使用整数比较。nexttoward与, 和nextafterf(float,float)/的一致性nextafterl(long double, long double)也很重要。我还没有研究过长的双重版本。整数比较在那里比较笨拙,因为int64_t不能容纳 80 位。

使用仅整数比较实现起来稍微慢一些/体积更大(使用 64 位整数比较以及 x 和 y 的符号,而不是仅使用 FP 比较和 x 的符号)。IEEE 浮点数设计巧妙,因此它们的位可以作为符号大小整数进行比较(因此可以作为 2 的补码整数进行比较,并检查是否为负数)。

nextafter在其他系统(OS X、MSVC++ 等)上表现如何?如果人们有兴趣测试它,我可能会编写一个小测试程序。

希望 nextafter表现如何?nextafter除了 DAZ/FTZ 之外,几乎完全指定了,因此不同的功能可能会更好地满足除此之外的愿望。)

我错过了什么吗?


我知道当前的实现对于这种很少使用的功能来说已经“足够好”了。它很丑陋,但它很少使用,因此没有太大影响。(其实它是很少用的吗?有没有人见过用它很多的代码?)

这里的部分动机是我喜欢用汇编语言优化东西,FP数据上的整数操作很有趣,特别是在x86-64上,整数指令可以用于向量寄存器,也用于floats和doubles(但不是80 位long doublenextafterl或 ) 的方向 arg nexttowards

0个回答
没有发现任何回复~