【问题标题】:Are floating point operations in C associative?C中的浮点运算是关联的吗?
【发布时间】:2012-12-04 11:33:15
【问题描述】:

加法在数学上保持关联属性:

(a + b) + c = a + (b + c)

在一般情况下,此属性不适用于浮点数,因为它们以有限精度表示值。

在从 C 程序生成机器代码作为优化的一部分时,是否允许编译器进行上述替换?它在 C 标准中的确切位置是什么?

【问题讨论】:

标签: c math floating-point compiler-optimization


【解决方案1】:

不允许编译器执行“优化”,这将导致计算的值与根据抽象机器语义计算的值不同。

5.1.2.3 程序执行

[#1] 这个International中的语义描述 标准描述了抽象机器的行为 哪些优化问题无关紧要。

[#3] 在抽象机中,所有表达式都被求值 由语义指定。

[#13] 示例 5 浮点表达式的重新排列 由于精度的限制,经常受到限制 以及范围。该实施不能普遍适用 加法的数学关联规则或 乘法,也不是分配规则,因为 舍入误差,即使没有溢出和 下溢。

在你的例子中:

(a + b) + c

甚至没有括号:

a + b + c

我们有

   +
  / \
  +  c
 / \
 a  b

编译器需要生成代码,就像ab 相加,结果与c 相加一样。

【讨论】:

  • 正确的理论。带回家的应用程序是:计算出你的操作应该以什么顺序发生(例如,如果你想从小到小,从大到大),放在括号中以满足偏执,你可以不用担心编译器会破坏事情重新排序。
  • 但是请注意,虽然运算符的优先级是明确定义的,但子表达式的求值顺序是未指定的。换句话说,程序可以从右到左或从左到右开始评估这个二叉树,它甚至不必以一致的方式保持这个顺序,也不需要记录它。因此,如果 a、b 或 c 中的任何一个包含影响结果的副作用,那么代码就会有问题。例如说 c 是一个修改 a 的函数:那么我们无法知道结果。
  • 请不要忘记:“在 ISO 标准中,示例无一例外是非规范性的”。来源:link.
  • @Lundin Re:“子表达式的评估顺序未指定”:(1)原理是什么? (2) 在 C11(或更新版本)的哪个页面上这样说?
  • @pmor 对于一般操作员,C17 6.5/3。或者对于具有相同含义的更好的文本,C99 6.5/3“子表达式的评估顺序和副作用发生的顺序都是未指定的。”
【解决方案2】:

您可以使浮点运算与 gcc 选项相关联:

-funsafe-math-optimizations -O2

例子:

double test (double a, double b, double c) {    
  return (a + b + c) * (a + (b + c));
}

这简化为:

double temp = a + (b + c);
return temp * temp;

同样,(a + b + c) - (a + (b + c)) 被归零,忽略了INFNAN 的可能性。

如果我改为使用 -fassociative-math -O2 编译,我会收到奇怪的消息:

警告:-fassociative-math 已禁用;其他选项优先

-funsafe-math-optimizations反正如果你不关心操作数的顺序可以提高速度,但是如果操作数的顺序很重要,它可能会导致精度损失,你可能会丢失NANINF结果。

【讨论】:

    【解决方案3】:

    C 中的浮点乘法不具有关联性。

    In C, Floating point multiplication is not associative.
    

    以下 C 代码有一些证据:

    选择三个随机浮点值。
    检查a*(b*c) 是否不等于(a*b)*c

    #include<stdio.h>
    #include<time.h>
    #include<stdlib.h>
    using namespace std;
    int main() {
        int counter = 0;
        srand(time(NULL));
        while(counter++ < 10){
            float a = rand() / 100000;
            float b = rand() / 100000;
            float c = rand() / 100000;
    
            if (a*(b*c) != (a*b)*c){
                printf("Not equal\n");
            }
        }
        printf("DONE");
        return 0;
    }
    

    程序打印:

    Not equal
    Not equal
    Not equal
    Not equal
    DONE
    RUN FINISHED; exit value 0; real time: 10ms; user: 0ms; system: 0ms
    

    结论:

    对于我的测试,三个随机选择的浮点乘法值在大约 70% 的时间是关联的。

    【讨论】:

    • OP 询问编译器在执行优化时是否假定浮点操作是关联的。他清楚地意识到他们实际上不是。
    • 是的,但是看到一个证实理论的例子总是很高兴。
    • 这不是关于 c 的,然而,它只是告诉你一些关于浮点数学的东西。任何符合 IEEE 标准的执行环境都将表现出相同的行为。
    猜你喜欢
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-15
    • 2011-03-07
    • 2016-11-22
    • 1970-01-01
    相关资源
    最近更新 更多