为什么这两个代码变体会产生不同的浮点结果?

2024-02-07

给出这个示例 C++ 代码片段:

void floatSurprise()
{
    // these come from some sort of calculation
    int a = 18680, b = 3323524, c = 121;
    float m = float(a) / c;

    // variant 1: calculate result from single expression
    float r1 = b - (2.0f * m * a) + (m * m * c);
    cout << "r1 = " << r1 << endl;

    // variant 2: break up the expression into intermediate parts, 
    /// then calculate 
    float
        r2_p1 = 2.0f * m * a,
        r2_p2 = m * m * c,
        r2 = b - r2_p1 + r2_p2;

    cout << "r2 = " << r2 << endl;
}

输出是:

开发者1 = 439703
开发2 = 439702

在调试器中查看时,这些值实际上分别是 439702.50 和 439702.25,这本身就很有趣 - 不知道为什么 iostream 默认情况下打印没有小数部分的浮点数。EDIT:原因是 cout 的默认精度设置太低,至少需要 cout

但我更感兴趣的是为什么我会得到不同的结果。我想这与舍入和整数与所需浮点输出类型的一些微妙相互作用有关,但我无法指出它。哪个值是正确的?

我很惊讶用这么简单的一段代码很容易搬起石头砸自己的脚。任何见解将不胜感激!编译器是VC++2010。

EDIT2:我使用电子表格进行了更多调查,为中间变量生成“正确”值,并发现(通过跟踪)它们确实被修剪,导致最终结果的精度损失。我还发现了单个表达式的问题,因为我实际上使用了一个方便的函数来计算平方而不是m * m there:

template<typename T> inline T sqr(const T &arg) { return arg*arg; }

尽管我很好地询问,编译器显然没有内联它,而是单独计算值,在将值返回到表达式之前修剪结果,再次扭曲结果。哎哟。


你应该阅读我很长很长的答案,了解为什么 C# 中会发生同样的事情:

(.1f+.2f==.3f) != (.1f+.2f).Equals(.3f) 为什么? https://stackoverflow.com/questions/15117037/1f-2f-3f-1f-2f-equals-3f-why/15117741#15117741

总结:首先,使用 float 只能获得大约小数点后七位的精度。如果您在整个计算过程中使用精确的算术对其进行计算,则正确答案约为 439702.51239669...因此,无论哪种情况,考虑到浮点数的限制,您都非常接近正确答案。

但这并不能解释为什么看似完全相同的计算却得到不同的结果。答案是:编译器有很大的自由度来进行数学计算更准确,显然您遇到了两种情况,优化器采用逻辑上相同的表达式,但不会将它们优化为相同的代码。

不管怎样,请仔细阅读我关于 C# 的回答;其中的所有内容也适用于 C++。

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

为什么这两个代码变体会产生不同的浮点结果? 的相关文章

随机推荐

  • Span 不能是嵌套局部变量。为什么这是一个限制?

    以下内容无法编译 既然这不是匿名方法 lambda 表达式或查询表达式 为什么这是一个限制 将文本作为 ref ReadOnlySpan 传递效果很好 void TestNestedSpan var text Some text AsRea
  • Colorbox 使灯箱在滚动时固定

    我使用 jquery colorbox 当页面内容较大并且打开colorbox时 然后颜色框随着页面内容滚动 我希望即使背景内容滚动也需要修复颜色框 请帮我解决这个问题 也许所有这些答案都来自 colorbox 的早期版本 但 fixed
  • Java 颠倒文本 - 错误还是功能?

    在使用 Java 字体类和 Swing 时 我将字体大小设置为负值 我发现这会使文本被颠倒绘制 这是一个错误还是一个功能 谁能解释为什么会发生这种行为 试试看 import java awt Font import java awt Gra
  • 在 aspx 中包含 C# 文件代码

    我想在我的位置执行以下代码C 页 我知道这里还有更多类似的问题 但我找不到可以帮助我的东西 但是我在第一行收到服务器错误 The server block is not well formed 代码是 WebIntegrationRestS
  • “好”数字的算法

    如果数字 x 的任意两个连续数字之和在 k 和 2k 之间 则给定数字 x 是 好 我需要找到一种算法 对于给定的数字 k 和给定的数字 n 找出存在多少个 好 n 位数字 我在 PHP 中为此实现了一个实现 但复杂性太大 我正在搜索所有这
  • 对指针调用 free 两次

    我在讲座中被教导 召唤free 两次使用指针真的非常非常糟糕 我知道这是一个很好的做法 将指针设置为NULL 在释放它之后 然而 我仍然没有听到任何关于为什么会这样的解释 据我了解 方法malloc 有效 从技术上讲 它应该跟踪它已分配并供
  • WebException:服务器违反了协议。 Section=ResponseStatusLine(调用 Marketo SOAP API)

    我正在尝试调用 Marketo SOAP Web 服务 通过 ASP NET C 我成功添加了 Web 服务引用并尝试使用以下代码行调用它 SuccessGetLead lead service getLead paramsgetlead
  • 使用显示时没有底部填充:网格和滚动

    我有一个容器div有一些填充 display grid and overflow auto放 当一个孩子div的高度比父级的高度大 并且出现一个滚动条 它会滚动 以便没有底部填充 这里有一个Fiddler https jsfiddle ne
  • 绘图中的对数色阶

    我正在尝试使用一些异常值来可视化数据Plotly and Python3 异常值导致色阶图例看起来很糟糕 只有很少的高数据点 但图例看起来很糟糕 2k 和 10k 之间的空间太大 所以问题是 如何改变右侧 颜色图例 的外观 见下图 以便它主
  • 仍然可以在节点 run_list 中指定确切的食谱版本吗?

    我的笔记中有这样的内容 run list recipe email protected cdn cgi l email protection 可以明确指定在节点 run list 中使用的说明书版本 但我无法让它工作 也找不到任何文档来说明
  • TryFrom<&[T]> 和 TryFrom> 有什么区别?

    似乎有两种方法可以尝试将向量转换为数组 或者通过切片 fn a 或直接 fn b use std array TryFromSliceError use std convert TryInto type Input Vec
  • 如何将图像从 Laravel 控制器发送到 API Rest [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我需要从存储中获取图像Laravel并将其从控制器发送到外部 API REST 我正在使用 guzzlehttp multipart 但 A
  • 在 JPA 中使用泛型 @OneToMany

    我有一个班级 分支 我想在这里将其用作 Dictionary 和 Link 类 Entity public class Branch
  • 捕获异常时可以使用gdb进行回溯吗?

    我刚刚开始使用 C 异常并希望得到正确的解决方案 我的想法是在捕获异常时生成某种回溯信息 最初我有类似的想法C 中异常的调用堆栈 https stackoverflow com questions 3222890 call stack fo
  • 如果没有找到任何元素,我可以防止 Cypress cy.get 失败吗?

    我正在使用赛普拉斯cy get抓取元素 但如果没有 我的测试就会失败 我不想让它失败 我希望它继续下去 测试只是列出存在的项目 如果有 const listItemTitle data cy component list item titl
  • 在Python中,是否有一种优雅的方法可以以自定义格式打印列表而无需显式循环?

    我知道你能做到 print str myList to get 1 2 3 你可以做 i 0 for entry in myList print str i entry i 1 to get 0 1 1 2 2 3 但是有没有一种与第一种类
  • canvas.toDataURL() 不是函数 - 错误 node-webGL 包装器

    目前我正在尝试将基于浏览器的客户端体积渲染代码转换为服务器端纯基于 JavaScript 的渲染 我在服务器端使用node webgl 我的主要目标是将服务器的画布内容发送到客户端 然后将该图像数据显示在客户端上 但在此之前我需要检查渲染是
  • mysql 5.7 日志慢查询错误

    我试图在 mysql 5 7 上启用慢查询日志记录并收到此错误 2016 04 27T14 55 51 934612Z 0 错误 未知变量 log slow queries var log mysql query log 2016 04 2
  • 谷歌地图删除所有标记,然后创建新的

    我正在创建一个地图 默认情况下加载地址并显示标记并在搜索框中放置地址 效果很好 但我需要添加单击事件 该事件将首先删除所有标记 然后放置标记 到目前为止 我正在开发满足我需要的所有脚本 但是 当用户单击地图时 搜索框会获取地点地址 但旧标记
  • 为什么这两个代码变体会产生不同的浮点结果?

    给出这个示例 C 代码片段 void floatSurprise these come from some sort of calculation int a 18680 b 3323524 c 121 float m float a c