C99 - 关于浮点舍入模式排序的保证
C99 - guarantees about ordering of floating-point rounding modes
是否可以保证(在 C99 标准 and/or 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)
Edit:原来这个具体的例子是GCC "feature"1:使用Clang,或者激活GCC的优化标志,或在调用 log10f
时使用文字常量而不是变量,所有这些都会产生一致的值。然而,问题仍然存在于一般情况下。
1 这不被 GCC 认为是错误,而是由于 GCC 直接执行优化而没有涉及不精确的 glibc 的令人惊讶的结果。
不提供此类保证。
一些数学库试图满足这个约束仅对数学库函数,但任何标准都不需要这种行为,而且这种情况很少见(即使大多数库尝试提供它有妥协和错误)。
对于不在数学库中的函数,试图符合 属性 几乎是不可能的,反正大部分都是无用的,因此基本上是闻所未闻的。
IEEE-754 (2008) 建议,但不要求正确舍入数学库函数的特定子集(或提供正确舍入的版本),这意味着 属性 您正在寻找。但是,该建议目前并未得到广泛实施。
是否可以保证(在 C99 标准 and/or 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)
Edit:原来这个具体的例子是GCC "feature"1:使用Clang,或者激活GCC的优化标志,或在调用 log10f
时使用文字常量而不是变量,所有这些都会产生一致的值。然而,问题仍然存在于一般情况下。
1 这不被 GCC 认为是错误,而是由于 GCC 直接执行优化而没有涉及不精确的 glibc 的令人惊讶的结果。
不提供此类保证。
一些数学库试图满足这个约束仅对数学库函数,但任何标准都不需要这种行为,而且这种情况很少见(即使大多数库尝试提供它有妥协和错误)。
对于不在数学库中的函数,试图符合 属性 几乎是不可能的,反正大部分都是无用的,因此基本上是闻所未闻的。
IEEE-754 (2008) 建议,但不要求正确舍入数学库函数的特定子集(或提供正确舍入的版本),这意味着 属性 您正在寻找。但是,该建议目前并未得到广泛实施。