侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

从float到double的转换产生不同的结果 – 相同的代码,相同的编译器,相同的OS

2023-04-06 星期四 / 0 评论 / 0 点赞 / 70 阅读 / 6215 字

编辑:有关答案的更新,请参阅问题的结尾. 我花了几个星期跟踪一个软件中的一个非常奇怪的错误保持.长话短说,有一个旧的软件分发,以及需要匹配输出的新软件旧.这两个人(在理论上)依赖于一个共同的图书

... . . 编辑:有关答案的更新,请参阅问题的结尾.

我花了几个星期跟踪一个软件中的一个非常奇怪的错误
保持.长话短说,有一个旧的软件
分发,以及需要匹配输出的新软件
旧.这两个人(在理论上)依赖于一个共同的图书馆.[1]但是,我不能
复制原始版本库生成的结果,
即使两个版本的库的源匹配.实际上
有问题的代码非常简单.原始版本看起来像这样(
“voodoo”评论不是我的):[2]

// float rstr[101] declared and initialized elsewhere as a globalvoid my_function() {    // I have elided several declarations not used until later in the function    double tt,p1,p2,t2;    char *ptr;    ptr = NULL;    p2 = 0.0;    t2 = 0.0; /* voooooodoooooooooo */    tt = (double) rstr[20];    p1 = (double) rstr[8];    // The code goes on and does lots of other things ...}

我所包含的最后一个陈述是不同行为的出现.在里面
原始程序,rstr [8]的值为101325.,并将其转换为
double [3]并指定它,p1的值为101324.65625.同样,tt
最终得到的值是373.149999999996.我用这个确认了这些值
调试打印和检查调试器中的值(包括检查)
十六进制值).这在任何意义上都不足为奇,它与预期的一样
浮点值.

在围绕相同版本的库(以及任何调用)的测试包装器中
到重构版本的库),第一个任务(到tt)
产生相同的结果.但是,p1最终为101325.0,与原始版本匹配
rstr中的值[8].这种差异虽小,但有时会产生实质性的影响
计算中的变化取决于p1的值.

我的测试包装很简单,并且与原始的包含模式相匹配
确切地说,但消除了所有其他背景:

#include "the_header.h"float rstr[101];int main() {    rstr[8] = 101325.;    rstr[20] = 373.15;    my_function();}

出于绝望,我甚至煞费苦心地看着
由VC6生成的反汇编.

4550:   tt = (double) rstr[20];0042973F   fld         dword ptr [rstr+50h (006390a8)]00429745   fstp        qword ptr [ebp-0Ch]4551:   p1 = (double) rstr[8];00429748   fld         dword ptr [rstr+20h (00639078)]0042974E   fstp        qword ptr [ebp-14h]

VC6为由相同的库函数生成的版本
测试代码包装器(与我重构的VC6生成的版本匹配
库的版本):

60:       tt = (double) rstr[20];00408BC8   fld         dword ptr [_rstr+50h (0045bc88)]00408BCE   fstp        qword ptr [ebp-0Ch]61:       p1 = (double) rstr[8];00408BD1   fld         dword ptr [_rstr+20h (0045bc58)]00408BD7   fstp        qword ptr [ebp-14h]

我能看到的唯一区别,除了内存中存储数组的位置和
通过这个程序发生了多远,是领先的_
在第二个参考rstr.通常,VC6使用前导下划线
使用函数进行名称修改,但我找不到它的任何文档
使用数组指针进行名称修改.我也无法理解为什么这些会产生
在任何情况下都会有不同的结果,除非涉及到名称错误
以不同的方式读取从指针访问的数据.

我可以在两者之间找出唯一的其他区别(除了打电话
context)是原来是一个基于MFC的Win32应用程序,而
后者是非MFC控制台应用程序.另外两个配置了
同样的方式,它们是用相同的编译标志构建的
相同的C运行时.

任何建议将不胜感激.

编辑:正如several answers非常有用地指出的那样,解决方案是检查二进制/十六进制值并比较它们以确保我认为完全相同的事实实际上是相同的.事实证明并非如此 – 尽管如此,我的强烈抗议却是相反的.

在这里,我可以吃一些不起眼的馅饼,并承认,虽然我认为我已经检查了这些值,但我实际上已经检查了一些其他密切相关的值 – 这一点我发现只有当我再次回顾数据时才发现.事实证明,在rstr [8]中设置的值略有不同,因此转换为double会突出显示非常微小的差异,然后这些差异会按照我所指出的方式在整个程序中传播.

初始化的差异我可以根据两个程序的工作方式来解释.具体来说,在一种情况下,rstr [8]是根据GUI的用户输入指定的(在这种情况下也是转换计算的结果),而在另一种情况下,它是从存储它的文件中读入的有些精度损失.有趣的是,在任何一种情况下,它实际上都不是101325.0,即使是从文件中读取它的情况,它也被存储为1.01325e5.

这将教会我仔细检查我对这些事情的双重检查.非常感谢Eric Postpischil和unwind提示我再次检查并提示及时反馈.这非常有帮助.

脚注

>实际上,原始的“库”是一个包含所有文件的头文件
内联完成的实现.标题是通过#include和
通过extern语句引用的函数.我把它修好了
重构版本的库实际上是一个库,但请参阅
其余的问题.
>请注意,变量名称不是我的,并且非常糟糕.同样的
使用全局变量,这在这个软件中很猖獗.我离开了
在/ * voooooodoooooooooo * /评论,因为它说明了……
不寻常……我的前任的编程实践.我认为这个元素是
因为这最初是从Fortran和开发人员翻译而来的
曾经用它来处理某种内存错误.这条线有
对代码的实际行为没有任何影响.
>我很清楚这里实际上并不需要演员,但是这个
原始库是如何工作的,我无法修改它.

.

解决方法

. 这个:

.

In the original program,rstr[8] has the value 101325.,and after casting it to double[3] and assigning it,p1 has the value 101324.65625

.

意味着浮点值实际上并不是101325.0,因此当您转换为double时,您会看到更多的精度.我会(高度)怀疑你检查浮点值的方法,自动(隐式和静默)舍入时,浮点数非常常见.检查位模式并使用系统中浮动的已知格式对其进行解码,以确保您不会被欺骗.

. . .. ...

广告 广告

评论区