2015-06-29 19 views
1

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

例如,假设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 “特征” :使用锵,或激活GCC的优化参数,或使用一个文本常量当调用log10f时,而不是变量,它们都会导致一致的值。然而,这个问题仍然存在于一般情况下。

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

+0

为什么选择C99?为什么不是“C”或“C11”? –

+0

主要是因为C99在'fenv.h'中引入了'FE_ *'舍入模式,但你说得对,这个问题不是C99特有的*本身*。 – anol

+2

您的示例程序从fenv.h调用函数,但它不包含'#pragma STDC FENV_ACCESS'。这意味着编译器可以按照自己的喜好进行操作。但是,如果你添加这个编译指示,GCC会告诉你它不支持它。据我记得,它并没有在警告消息中告诉你它在命令行中支持文件相当于这个编译指示,但它确实支持这样一个命令行标志,''-frounding-math',并且如果你是幸运的是你不会发现它的一个错误。 –

回答

4

没有这样的保证。

一些数学库,试图满足数学库函数只这个约束,但没有任何标准要求的行为,这是很罕见的(试图提供它具有的妥协和错误甚至大部分库)。

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

IEEE-754(2008)建议,但并不要求数学库函数的特定子集被正确舍入(或正确舍入的版本可用),这意味着你的属性寻找。但是,这一建议目前尚未得到广泛实施。

+0

C标准的任何版本是否都指定了有关计算的浮点模式变化*排序*的任何内容?根据我的理解,给定'double x = 1/w + foo(); double y = 1/w + bar(); double z = 1/w + boz();'编译器有权将'1/w'优化为一个常见的子表达式,即使它对'bar'一无所知。如果'bar'改变浮点模式,那么在计算'z'的(1/w)项之前会有什么保证变化会生效?除非实施提供超出标准的保证,否则舍入模式是否可靠? – supercat