C 从浮点到双精度的转换产生不同的结果—;相同的代码,相同的编译器,相同的操作系统

C 从浮点到双精度的转换产生不同的结果—;相同的代码,相同的编译器,相同的操作系统,c,assembly,x86,visual-c++-6,disassembly,C,Assembly,X86,Visual C++ 6,Disassembly,编辑:有关答案的更新,请参见问题末尾 我花了几个星期的时间在一个我喜欢的软件中追踪一个非常奇怪的错误 维持。长话短说,有一个古老的软件正在开发中 发行版,以及需要与 古老的两者(理论上)依赖于一个公共图书馆。[1]然而,我不能 复制由库的原始版本生成的结果, 即使库的两个版本的源代码匹配。实际的 所讨论的代码非常简单。原来的版本是这样的 “巫毒”不是我的):[2] 我最后一句话是不同的行为出现的地方。在 原始程序,rstr[8]具有值101325.,并在将其转换为 double[3]并将其赋值,

编辑:有关答案的更新,请参见问题末尾

我花了几个星期的时间在一个我喜欢的软件中追踪一个非常奇怪的错误 维持。长话短说,有一个古老的软件正在开发中 发行版,以及需要与 古老的两者(理论上)依赖于一个公共图书馆。[1]然而,我不能 复制由库的原始版本生成的结果, 即使库的两个版本的源代码匹配。实际的 所讨论的代码非常简单。原来的版本是这样的 “巫毒”不是我的):[2]

我最后一句话是不同的行为出现的地方。在 原始程序,
rstr[8]
具有值
101325.
,并在将其转换为
double
[3]并将其赋值,
p1
的值为
101324.65625
。类似地,
tt
以值
373.1499999996
结束。我已经与您确认了这些值 调试打印和检查调试器中的值(包括检查 十六进制值)。这在任何意义上都不足为奇,正如我们所预料的那样 浮点值

在库的同一版本的测试包装器中(以及在任何调用中 对于库的重构版本),第一个赋值(to
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为重构生成的版本相匹配) 库的版本):

除了数组在内存中的存储位置和 这种情况在程序中发生的时间有多远 第二节中的参考
rstr
。一般来说,VC6使用前导下划线作为 名称与函数有冲突,但我找不到任何关于它的文档 名称与数组指针冲突。我也不明白为什么会产生这样的结果 在任何情况下都会产生不同的结果,除非这个名字与此有关 以不同的方式读取从指针访问的数据

我能确定的唯一区别是两者之间的区别(除了打电话 上下文)是指原始的是基于MFC的Win32应用程序,而 后者是非MFC控制台应用程序。这两个都是以其他方式配置的 同样,它们使用相同的编译标志并根据 相同的C运行时

如有任何建议,将不胜感激


编辑:正如非常有帮助地指出的那样,解决方案是检查二进制/十六进制值并进行比较,以确保我认为完全相同的东西实际上是相同的。尽管我强烈反对,但事实证明情况并非如此

在这里,我谦虚地承认,虽然我认为我已经检查了这些值,但实际上我已经检查了其他一些密切相关的值——这一点我是在再次查看数据时才发现的。事实证明,在
rstr[8]
中设置的值稍有不同,因此转换为double突出显示了非常细微的差异,然后这些差异以我注意到的方式在整个程序中传播

我可以根据两个程序的工作方式解释与初始化的差异。具体地说,在一种情况下,
rstr[8]
是基于用户对GUI的输入指定的(在这种情况下也是转换计算的产物),而在另一种情况下,它是从存储在其中的文件中读取的,精度有所降低。有趣的是,在这两种情况下,它实际上都不完全是
101325.0
,甚至是从存储为
1.01325e5
的文件中读取的情况

这将教会我对这类事情进行双重检查。非常感谢和鼓励我再次检查,并及时反馈。这很有帮助


脚注
  • 实际上,最初的“库”是一个包含所有 实现是内联完成的。标题通过
    #include
    和 通过
    extern
    语句引用的函数。我已经用一种方法解决了这个问题 该库的重构版本实际上是一个库,但请参见 问题的其余部分
  • 请注意,变量名不是我的,而且很糟糕。同样地 使用全局变量,这在这个软件中非常普遍。我走了 在
    /*voooooooooo*/
    注释中,因为它说明了… 不寻常的…我的前任的编程实践。我认为这一点是正确的 因为它最初是从Fortran和开发人员翻译过来的 用它来对付某种内存错误。电话线已经接通了 对代码的实际行为没有任何影响
  • 我很清楚这里实际上不需要演员阵容,但是这个 是原始库的工作方式,我无法修改它
  • 这:

    在原始程序中,
    rstr[8]
    的值为101325,将其转换为
    double[3]
    并赋值后,p1的值为
    101324.65625

    这意味着
    float
    值实际上不是101325.0,所以
    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]
    
    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]