当前位置: 首页 > 知识库问答 >
问题:

参数顺序到std::min更改浮点的编译器输出

仲孙默
2023-03-14

我在编译器资源管理器中摆弄了一下,发现传递给std::min的参数的顺序改变了发出的程序集。

下面是Godbolt编译器资源管理器的示例

double std_min_xy(double x, double y) {
    return std::min(x, y);
}

double std_min_yx(double x, double y) {
    return std::min(y, x);
}

对其进行编译(例如,在clang 9.0.0上使用-O3),以便:

std_min_xy(double, double):                       # @std_min_xy(double, double)
        minsd   xmm1, xmm0
        movapd  xmm0, xmm1
        ret
std_min_yx(double, double):                       # @std_min_yx(double, double)
        minsd   xmm0, xmm1
        ret

我的问题是:为什么编译器不切换参数本身的顺序,这样movapd就不是必需的了?它肯定知道minsd的参数顺序不会改变答案?是不是有什么副作用我不明白?

共有1个答案

毛弘博
2023-03-14

minsd a,b对于某些特殊的FP值是不可交换的,std::min也是不可交换的,除非您使用-ffast-math

MINSD a,b完全实现了(a 包括严格的IEEE-754语义中暗示有符号零和NaN的所有内容。(即,它将源操作数 b保留在无序的 1或相等)。正如Artyer所指出的, -0.0+0.0比较相等(即 -0.<0.是假的),但它们是不同的。

std::min是根据(a 比较表达式(cppreference)定义的,使用 (a std::fmin 不同, std::fmin保证从任一操作数进行NaN传播。( fmin最初来自C数学库,而不是C++模板。)

看看在x86上给出无分支FP最小值和最大值的指令是什么?有关minss/minsd/maxss/maxsd(以及相应的内部规则,除了某些GCC版本外,它们遵循相同的非交换规则)的更多详细信息

脚注1:记住nan 对于任何 b和任何比较谓词都是false。例如, nan==b是false, nan>b也是false。即使 nan==nan也是false。当一对中的一个或多个是NaN时,它们是“无序”WRT。彼此。

使用-ffast-math(告诉编译器不假设NAN,以及其他假设和近似值),编译器将两个函数都优化为一个minsd。https://godbolt.org/z/a7ok91

对于GCC,请参见https://GCC.gnu.org/wiki/floatingpointmath
clang支持类似的选项,包括-ffast-math作为一个总括选项。

这些选项中的一些几乎每个人都应该启用,但奇怪的遗留代码库除外,例如-fno-math-errno。(有关推荐的数学优化的更多信息,请参阅本问答)。而且gcc-Fno-traping-math是一个好主意,因为尽管默认为打开状态,但它仍然不能完全工作(某些优化仍然可以更改取消屏蔽异常时将引发的FP异常的数量,包括有时甚至从1变为0或0变为非零,IIRC)。gcc-ftrapping-math还会阻止一些即使是WRT也是100%安全的优化。异常语义,所以它很糟糕。在不使用fenv.h的代码中,您永远不会知道其中的区别。

但是,将std::min视为可交换的,只能通过不假设NAN的选项或类似的东西来实现,因此对于那些关心NAN发生了什么的代码来说,绝对不能被称为“安全的”。例如,-ffinite-math-only不假设nan(也不假设无穷大)

clang-funsafe-math-optimizations-ffinite-math-only将执行您所要的优化。(unsafe-math-optimizations意味着一系列更具体的选项,包括不关心带符号的零语义)。

 类似资料:
  • 本章将介绍Rust编译器的参数。 Rust编译器程序的名字是rustc,使用它的方法很简单: $ rustc [OPTIONS] INPUT 其中,[OPTIONS]表示编译参数,而INPUT则表示输入文件。而编译参数有以下可选: -h, --help - 输出帮助信息到标准输出; --cfg SPEC - 传入自定义的条件编译参数,使用方法如 fn main() { if cfg!(he

  • 我想使用部分模板专门化,以便将数组(在编译时创建)“分解”为由其值组成的参数包(以便与我在代码中定义的其他结构接口)。以下内容(我的第一次尝试)不编译 因为模板参数不得涉及模板参数。正如我所理解的,在部分模板专门化中提供非类型参数是不可能的,如果它们不是非常依赖于模板参数的话。因此,我试图通过在类型中包含值来解决这个问题: 我使用并通过,因为我使用非类型模板参数,所以需要c++2a。 在我的实际代

  • 问题内容: 之后,我用来获取输入参数。 但是,使用pyinstaller编译程序后,exe程序将无法接受我的输入。 它将仅使用我为程序设置的默认值。如何编译它并让exe文件接受我的输入? 我希望我不需要为此编写GUI。抱歉回复晚了。这是代码: 如果我使用“ python this_script.py”运行它,它将等待我输入等待的秒数。但是用pyinstaller编译后,我双击了exe文件。我没有地

  • 根据C编程语言-第4节,第6.2.5节: 有三种浮点类型:浮点(单精度)、双精度(双精度)和长双精度(扩展精度) 参考:http://en.wikipedia.org/wiki/Single-precision_floating-point_format 真有效位包括二进制点右侧的23个分数位和值为1的隐式前导位(二进制点左侧),除非指数存储为全零。因此,内存格式中只有23个有效位的分数位,但总精

  • 在Stroustrup的新书《C编程语言-第四版》第10.5.1节中,他说,在执行算术运算之前,整数提升用于从较短的整数类型中创建整数,类似地,浮点提升用于从浮点中创建双精度。 我用以下代码确认了第一个索赔: 这将输出带有vc的“int”和带有gcc的“i”。 但是,用浮点而不是短路进行测试,输出仍然是“浮点”或“f”: 根据Stroustrup的说法,浮点提升规则没有例外,所以我希望输出“dou

  • 现在,我有下面的代码,它只是测试标准库中的< code>std::set_difference: 当我用Clang/GCC编译它时,我得到了输出: 更新我想实际使用的源代码案例(假设是我将要做的一些操作。因此,这些操作将按的顺序发生): 好吧,这看起来不错,它实际上对应于标准::set_difference,检查链接:但是当我选择MSVC时,我得到了不同的输出(检查链接:https://godbo