MinGW GCC 4.9.1 和浮点确定性

2024-01-12

我编写了一个小程序来计算 3 坐标向量的欧几里得范数。这里是:

#include <array>
#include <cmath>
#include <iostream>

template<typename T, std::size_t N>
auto norm(const std::array<T, N>& arr)
    -> T
{
    T res{};
    for (auto value: arr)
    {
        res += value * value;
    }
    return std::sqrt(res);
}

int main()
{
    std::array<double, 3u> arr = { 4.0, -2.0, 6.0 };
    std::cout << norm(arr) - norm(arr) << '\n';
}

在我的电脑上,它打印-1.12323e-016.


我知道应该小心处理浮点类型。然而,我认为浮点运算至少在某种程度上是确定性的。本文 http://randomascii.wordpress.com/2013/07/16/floating-point-determinism/关于浮点决定论指出:

一些被保证的东西是加法、减法、乘法、除法和平方根的结果。这些操作的结果保证是正确舍入的精确结果(稍后会详细介绍),因此,如果您提供相同的输入值、相同的全局设置和相同的目标精度,则可以保证获得相同的结果。

正如您所看到的,该程序对浮点值执行的唯一操作是加法、减法、乘法和平方根。如果我相信上面引用的文章,考虑到它在单线程中运行并且我不更改舍入模式或任何其他与浮点相关的内容,我认为norm(arr) - norm(arr)将会0因为我对相同的值执行了两次完全相同的操作。

我的假设是错误的,还是编译器不严格遵守 IEEE 浮点数学的情况?我目前正在使用 MinGW-W64 GCC 4.9.1 32 位(我尝试了从-O0 to -O3)。显示与 MinGW-W64 GCC 4.8.x 相同的程序0,这正是我所期望的。


EDIT:我把代码反汇编了。我不会发布整个生成的程序集,因为它太大了。但是,我相信相关部分在这里:

call    ___main
fldl    LC0
fstpl   -32(%ebp)
fldl    LC1
fstpl   -24(%ebp)
fldl    LC2
fstpl   -16(%ebp)
leal    -32(%ebp), %eax
movl    %eax, (%esp)
call    __Z4normIdLj3EET_RKSt5arrayIS0_XT0_EE
fstpl   -48(%ebp)
leal    -32(%ebp), %eax
movl    %eax, (%esp)
call    __Z4normIdLj3EET_RKSt5arrayIS0_XT0_EE
fsubrl  -48(%ebp)
fstpl   (%esp)
movl    $__ZSt4cout, %ecx
call    __ZNSolsEd
subl    $8, %esp
movl    $10, 4(%esp)
movl    %eax, (%esp)
call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
movl    $0, %eax
movl    -4(%ebp), %ecx
.cfi_def_cfa 1, 0
leave

如你看到的,__Z4normIdLj3EET_RKSt5arrayIS0_XT0_EE被调用两次,因此,它不是内联的。但我不明白整个事情,也不知道问题出在哪里。


正如@MatthiasB 指出的,这似乎是 gcc 将 80 位浮点值临时存储到 64 位寄存器/内存位置的问题。考虑以下仍然重现该问题的简化程序:

#include <cmath>
#include <iostream>

double norm() {
    double res = 4.0 * 4.0 + (-2.0 * -2.0) + (6.0 * 6.0);
    return std::sqrt(res);
}

int main() {
    std::cout << norm() - norm() << '\n';
    return 0;
}

关键部分的汇编代码norm() - norm()看起来像这样(使用32位mingw 4.8.0编译器)

...
call    __Z4normv     ; call norm()
fstpl   -16(%ebp)     ; store result (80 bit) in temporary (64 bit!)
call    __Z4normv     ; call norm() again
fsubrl  -16(%ebp)     ; subtract result (80 bit) from temporary (64 bit!)
...

本质上,我认为这是一个 gcc bug,但它似乎是一个复杂的话题 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=12331 ...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MinGW GCC 4.9.1 和浮点确定性 的相关文章

  • 不同提供商的相同 EDMX 文件

    我正在开发一个项目 其中有一个本地数据库 SQL CE 在不存在与服务器的连接的情况下用作缓冲区 在服务器上我想使用相同的数据库布局 当然 我想使用服务器和客户端上可用的 Common dll 中的相同 EDMX 文件 在客户端中 我有一个
  • 获取 TextBox 中的文本行数

    我试图通过标签显示文本框中的文本行数 但是 问题是如果最后一行为空 标签必须显示没有空行的行号 例如 如果它们有 5 行 最后一行为空 则标签应将行数显示为 4 Thanks private void txt CurrentVinFilte
  • 使用sqlbulkcopy之前如何创建表

    我有一个 DBF 文件 我正在尝试导入该文件 然后将其写入 SQL 表 我遇到的问题是 如果我使用 SqlBulkCopy 它需要我提前创建表 但在我的场景中这是不可能的 因为 dbf 文件不断变化 到目前为止 这是我的代码 public
  • 如何向WebRequest添加参数?

    我需要从 Web 服务调用一个方法 所以我编写了以下代码 private string urlPath http xxx xxx xxx manager string request urlPath index php org get or
  • C++ 中的“int”默认是“signed long int”吗?

    Is int默认情况下signed long int in C 它是否依赖于平台和 或编译器 如果是这样 怎么办 EDIT 以下任何一项是否保证是重复的 signed short int signed int signed long int
  • C++0x 初始值设定项列表示例

    我想看看这个现有代码示例如何利用 C 0x 初始化列表功能 示例0 include
  • 使用 GCHandle 将大型结构数组从 C# unity 脚本传递到 C++ dll 在 C++ 函数执行后崩溃

    我想从 C unity 脚本将结构数组传递给 c 本机插件 我做了如下操作 我可以访问数据 但我的应用程序在执行 c 函数后崩溃 我不知道为什么 C side StructLayout LayoutKind Sequential publi
  • std::bind2nd 和 std::bind 与二维数组和结构数组

    我知道 C 有 lambda 并且 std bind1st std bind2nd 和 std bind 已弃用 然而 从C 的基础开始 我们可以更好地理解新特性 所以 我从这个非常简单的代码开始 使用int 数组s 第一个例子 与std
  • 如何在 ASP.NET Core 6.0 Web API 项目中启用 cors?

    在我的 ASP NET Core 6 0 Web API 项目中配置了 CORS 但预检请求收到 http 405 错误 换句话说 不允许使用 HTTP OPTION 看起来 cors 没有启用 我见过的例子config EnableCor
  • 使用默认行为将模型绑定到接口

    我正在尝试将控制器操作绑定到接口 但仍保持默认的绑定行为 public class CoolClass ISomeInterface public DoSomething get set ISomeInterface public clas
  • 打开位置设置页面或提示用户启用位置

    我一直在绞尽脑汁 徒劳地谷歌搜索 我正在尝试找到一种方法来提示用户通过直接进入设置页面或仅点击屏幕上的 是 来切换位置 我见过的所有代码似乎都不起作用 有人有有效的方法吗 一个详细的例子将不胜感激 谢谢 我对 Xamarin 开发非常陌生
  • 应用新设置时如何防止 GraphicsDevice 被丢弃?

    我的游戏窗口允许手动调整大小 这意味着它可以像任何其他普通窗口一样通过拖动其边缘来调整大小 游戏还利用了RenderTarget2D rt2d 在主 Draw 方法中设置主渲染目标 GraphicsDevice SetRenderTarge
  • 多个线程访问一个变量

    我在正在读的一本教科书中发现了这个问题 下面也给出了解决方案 我无法理解最小值怎么可能是 2 为什么一个线程不能读取 0 而所有其他线程都执行并写入 1 而无论是1还是2 最后写入的线程仍然必须完成自己的循环 int n 0 int mai
  • 从二进制文件读取字节到 long int

    我有两个问题 我有二进制文件的数据 我想使用 read 函数读取前 8 个字节以签署 long int 但我不能 你知道我该怎么做吗 如何直接读取一块数据到字符串中 我可以像所示那样阅读吗 前任 ifstream is is open te
  • 确定相关词的编程方式?

    使用网络服务或软件库 我希望能够识别与词根相关的单词 例如 座位 和 安全带 共享词根 座位 但 西雅图 不会被视为匹配 简单的字符串比较对于这类事情似乎是不可行的 除了定义我自己的字典之外 是否有任何库或 Web 服务不仅可以返回单词定义
  • 使用联合对 IP 地址进行多种解释?

    在工作中 我们使用以下构造来将 IP 地址解释为 4 字节数组或 32 位整数 union IPv4 std uint32 t ip std uint8 t data 4 这很好用 但是读完这本书的第 97 章 不要使用联合来重新解释表示
  • 文本框中“结束编辑”的事件

    我正在 winform c 中使用文本框 并使用文本在数据库中进行查询 但每次文本更改时 我都需要不断查阅文本框的文本 因此 对于这些 我使用 KeyUp 但这个活动太慢了 文本框编辑完成后是否会触发任何事件 我考虑完成2个条件 控制失去焦
  • 如何阻止 Control-I 在 CoreWindow 范围内的 UWP 文本框中插入选项卡?

    当我在 UWP 应用程序中有一个 TextBox 时 对我来说 奇怪的行为 在 Windows 10 中创建通用的空白应用程序 UWP 应用程序 使用以下代码将文本框添加到默认网格
  • 使用 WinAPI 连接禁用的显示设备

    我的问题是启用禁用的监视器ChangeDisplaySettingsEx 我想这不是火箭科学 但经过一番挖掘后 它看起来仍然是不可能的 我找到了一种根据找到的 Microsoft 代码示例禁用所有辅助显示器的方法here https msd
  • 按 Enter 继续

    这不起作用 string temp cout lt lt Press Enter to Continue cin gt gt temp cout lt lt Press Enter to Continue cin ignore 或更好 in

随机推荐