从 C# 调用 Delphi 6 DLL 会导致不同的舍入

本文关键字:舍入 调用 Delphi DLL | 更新日期: 2023-09-27 18:36:23

当值似乎落在 .5 括号内时,即它是向上舍入还是向下舍入时,我在 delphi 舍入(从 c# 项目中使用时)及其结果的一致性遇到了一些严重的问题???

我创建了一个执行许多操作的 delphi DLL。 其中一个操作的关键是(例如)

double := Round(double1   * double2);

其中 double1 = 1.9 和 double2 = 225(从文件中读取这些值)

我的计算器上的结果显示它应该是 427.5。 德尔福 DLL 中的结果显示 427。 四舍五入,一切都很好,根据 Round() 的德尔菲文档

这些计算的值是针对代表形式的德尔菲(我相信的类)存储的。 在上面这样的情况下

property SRDairy           : Double   read FSRDairy             write FSRDairy;

此属性属于一堆类,并像这样序列化到 Delphi DLL 中的缓冲区(AOvrFarm 是包含所有要序列化的项目的父项,包括我的 SRDairy 项目)

procedure GetResultString(AOvrFarm: TdataV6; outputStringBuffer: PChar; var
    bufLen: Integer);
var
  MyStrings : TStringList;
  resultString   : string;
begin
  MyStrings := TStringList.Create;
  try
    AOvrFarm.SavetoStrings(MyStrings);
    resultString := MyStrings.Text;
    if outputStringBuffer = nil then
    begin
      bufLen := Length(resultString) + 1;
    end
    else
    begin
      StrLCopy(outputStringBuffer, PChar(resultString), bufLen - 1);
    end;
  finally
    MyStrings.Free;
  end;
end;

在这个DLL中还有很多其他的属性,窗体(类)等。 最后,此文件被序列化为一个字符串,该字符串返回到调用方 (c#) 应用程序,并使用类似 (在 delphi 中) 的内容填充 refoutputStringBuffer:

  StrLCopy(outputStringBuffer, PChar(resultString), bufLen - 1);

我的问题是当我从 c# 应用程序调用此 DLL 时。 我得到的结果不是 427,而是 428。 四舍五入!!!

德尔福签名:

函数 ConvertString(fileContent: PChar; fileExt: PChar; var outputStringBuffer: 普哈尔;var outputStringBufferSize: Integer;var errorMsgBuffer: PChar;变量 错误MsgBufferSize: Integer): WordBool;标准呼叫;出口;

我像这样使用我的 c# 中的 DLL:

[DllImport("OvrFileImport.dll",
               CallingConvention = CallingConvention.StdCall,
               CharSet = CharSet.Ansi)]
public static extern bool
        ConvertString(
        string fileContents, 
        string fileExt, 
        ref string refputStringBuffer,
        ref int outputStringBufferSize, 
        ref string errorMsgBuffer, 
        ref int errorMsgBufferSize);
调用

转换字符串一次以获取缓冲区大小,然后再次调用以填充缓冲区。 在第二次通话中,我注意到了差异。 当使用来自测试 Delphi 项目的相同 DLL 时,我得到 427(而不是 c# 428)。

我尝试针对任何 CPU 构建我的 c#,并且只有 x86,以防它是某种 cpu 64 位问题,因为我的 PC 是一台 64 位机器。

有没有人遇到过这种事情,如果有的话,他们有什么办法吗?

编辑 - 答案

正如David Heffernan所提到的,从c#调用DLL时使用的控制词与从另一个Delphi应用程序调用时使用的控制词不同。 从 c# 调用时使用的控制字实际上是 639 美元。

不幸的是,将控制字设置为 $1372 在调试时在我的 c# 应用程序中引起了问题,即它导致本地的监视窗口显示所有变量的"由于堆栈溢出状态而无法评估"异常。 我找到了一篇文章 - 奇怪的浮点结果,它谈到了这一点,因此将其更改为 $133F(它解决了浮点差异和 c# 调试问题)。

代码是:

begin
     cw := Get8087CW;
     Set8087CW($133F);
     ....
     finally
        Set8087CW(cw);    

从 C# 调用 Delphi 6 DLL 会导致不同的舍入

1.9 在浮点中不能完全表示,而 225 在浮点中是完全可表示的。

目前尚不清楚输入数字来自哪里,但IEEE754算术根本不可能精确地执行乘积 1.9*225。您看到的行为与四舍五入无关,实际上完全与可代表性有关。如果您需要精确地执行此计算,则需要使用十进制算术而不是浮点数。这意味着在 Delphi 中使用货币类型或在 C# 中使用十进制。

您的不同行为的来源可能归结为控制浮点寄存器的 8087 控制字。当 DLL 执行时,从 C# 调用,控制字将与默认的 Delphi 设置不同。在 DLL 的入口点调用 Set8087CW($ 1372)以使用默认的 Delphi 控制字。请记住在从 DLL 返回之前还原它。