【问题标题】:C99 - guarantees about ordering of floating-point rounding modesC99 - 保证浮点舍入模式的排序
【发布时间】:2015-09-16 02:35:46
【问题描述】:

是否有任何保证(在 C99 标准和/或 IEEE-754 中)使用不同舍入模式时获得的结果应该以特定方式排序?

例如,让f(rm, x) 是一个函数,其中rm 是舍入模式,x 是它的参数。我可以认为没有错误的实现应该确保以下不等式吗?

f(FE_DOWNWARD,x) <= f(FE_TONEAREST,x) <= f(FE_UPWARD,x)

例如,我机器上的以下代码与这个假设相矛盾(即使使用最近的 glibc,版本 2.21),我想知道这是一个错误(值得报告),还是仅仅是舍入错误的不幸后果,这意味着这种行为不应该被依赖。

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <fenv.h>
#include <gnu/libc-version.h>

void set_round(int rm) {
  // checks that the rounding mode has been successfully set
  if (fesetround(rm)) { perror("setround"); exit(1); }
}

int main() {
  printf("GNU libc version: %s\n", gnu_get_libc_version());
  float x = 3;
  set_round(FE_TONEAREST);
  float r = log10f(x);
  printf("nearest:     r = %g (%a)\n", r, r);

  set_round(FE_DOWNWARD);
  r = log10f(x);
  printf("downward:    r = %g (%a)\n", r, r);

  set_round(FE_UPWARD);
  r = log10f(x);
  printf("upward:      r = %g (%a)\n", r, r);

  set_round(FE_TOWARDZERO);
  r = log10f(x);
  printf("toward zero: r = %g (%a)\n", r, r);
  return 0;
}

输出:

GNU libc version: 2.21
nearest:     r = 0.477121 (0x1.e89278p-2)
downward:    r = 0.477121 (0x1.e8927ap-2)
upward:      r = 0.477122 (0x1.e8927cp-2)
toward zero: r = 0.477121 (0x1.e8927ap-2)

编辑:事实证明,这个特定示例是 GCC “功能”1:使用 Clang,或激活 GCC 的优化标志,或使用文字常量而不是调用log10f 时的变量,所有这些都会产生一致的值。然而,这个问题仍然存在于一般情况下。

1 这不是 GCC 的错误,而是一个令人惊讶的结果,因为 GCC 直接执行优化,而不涉及不精确的 glibc。

【问题讨论】:

  • 为什么是 C99?为什么不是“C”或“C11”?
  • 主要是因为 C99 在 fenv.h 中引入了 FE_* 舍入模式,但您说得对,问题不是 C99 特定的本身
  • 您的示例程序从 fenv.h 调用函数,但它不包含 #pragma STDC FENV_ACCESS。这意味着允许编译器为所欲为。但是,如果你添加这个 pragma,GCC 会告诉你它不支持它。据我记得,它并没有在警告消息中告诉您它在命令行上支持与此 pragma 等效的文件,但它确实支持这样的命令行标志 -frounding-math,如果你幸运的话,你会找不到错误。

标签: c floating-point language-lawyer c99 floating-accuracy


【解决方案1】:

不做这样的保证。

一些数学库试图满足这个约束仅适用于数学库函数,但是任何标准都不需要这种行为,而且这种情况非常罕见(即使大多数试图提供它的库也有妥协和错误)。

对于不在数学库中的函数,尝试符合该属性几乎是不可能的,而且基本上是无用的,因此基本上闻所未闻。

IEEE-754 (2008) 建议,但不要求正确舍入数学库函数的特定子集(或提供正确舍入的版本),这意味着您正在寻找的财产。但是,该建议目前并未得到广泛实施。

【讨论】:

  • C 标准的任何版本是否指定了关于浮点模式更改的排序 与计算相关的任何内容?据我了解,给定double x=1/w+foo(); double y=1/w+bar(); double z=1/w+boz();,即使编译器对bar 一无所知,它也有权将1/w 优化为公共子表达式。如果bar 改变了浮点模式,有什么保证在计算z 的(1/w)项之前更改会生效?除非实现提供超出标准的保证,否则舍入模式是否可靠相关?
猜你喜欢
  • 2011-10-15
  • 2015-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-05
相关资源
最近更新 更多