C++ VBA 浮点差异中的 .NET COM 组件

C++ .NET COM component in VBA floating point differences

本文关键字:NET COM 组件 VBA C++      更新时间:2023-10-16

我写了一个可以从VBA调用的C++.NET组件。该组件计算了一个非常大的数字数组(给定一些输入(,并将该数组写入二进制文件。

我注意到,如果我使用通过 VBA 调用组件的完全相同的设置生成文件,我每次都会得到不同的结果。在十六进制编辑器中打开二进制文件时,我可以看到字节的差异。但是,如果我做同样的事情,但通过在 C++.NET 或 C#.NET 可执行文件中调用组件,我根本不会得到任何差异 - 使用相同的设置生成的文件每次以这种方式生成时都是相同的。

差异很小,只是浮点差异。但是,我想找出为什么会发生这种情况。为什么通过 VBA 调用它时会出现差异,而不是通过 C++.NET 或 C#.NET 调用它?

作为记录 - 我在这里的 32 位机器上运行 64 位 Excel(用于 VBA((如果这有什么区别......

对此

的任何想法将不胜感激。

@Terry - 我想我能够证明这种情况正在发生。首先让我解释一下问题是什么(或者至少我认为是合理的解释(。

为什么 C# 可执行文件会给 Excel 不同的结果?

任何

应用程序都可以将浮点状态设置为所需的任何状态。事实上,这是一种很好的做法,因此您可以确保应用程序中的特定精度(如果重要的话(。

从 C# 可执行文件运行时,使用的浮点设置是编译器决定的任何设置。但是,当您在 Excel 中运行它时,Excel 会将浮点设置更改为所需的任何设置,这可能与可执行文件不同。这就是为什么你会有所作为。

为什么在 Excel 中运行每次可能会给出不同的结果?

Excel 将浮点设置更改为所需的任何设置。但是,它仅在需要使用的线程上执行此操作。因此,如果 Excel 需要 N 个线程,它将在这 N 个线程上设置浮点设置。任何新生成的线程上的浮点设置都不会从"主"线程复制。因此,如果加载项中的进程需要 M> N 个线程,则生成 M-N 个线程,这些线程的浮点设置与 Excel 设置的 N 个浮点设置不同。所以现在你有 M 个线程,它们有两组不同的浮点设置。由于并行编程中使用的线程不是确定性的,因此每次在 Excel 中运行时,可能会得到不同的结果。

我是如何解决的?

我通过在用于确保它始终相同的每个线程上显式设置浮点设置来解决此问题。我介绍了以下例程。它在C++中,但你也应该能够让它在 C# 中工作。

[DllImport("msvcrt.dll")] int _controlfp(int IN_New, int IN_Mask);
void FixFPU()
{
    _controlfp(_MCW_DN, _DN_SAVE);
    _controlfp(_MCW_EM, _EM_INVALID);
    _controlfp(_MCW_RC, _RC_CHOP);
    _controlfp(_MCW_PC, _PC_53);
}

我在 Parallel.For 循环中调用了这个例程。这可确保 Parallel.For 循环使用的每个线程都具有相同的浮点设置。通过显式指定浮点状态,可以在例程从外接程序运行时覆盖这些线程上的 Excel 浮点设置。我还在进程开始时调用了此例程,以确保启动时使用的单个线程也运行相同的浮点设置。这可确保在到达 Parallel.For 循环之前完成的任何计算都以与 Parallel.For 循环内相同的精度执行。

这是该例程中的最后一条语句,它会产生最大的差异,因为这设置了精度。

我希望这可以帮助您解决问题。