3

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

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

double := Round(double1   * double2);

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

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

这些计算的值是针对代表表单的 Delphi(我相信的类)存储的。在上面这样的情况下

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);

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

德尔福签名:

函数 ConvertString(fileContents: PChar; fileExt: PChar; var outputStringBuffer: PChar; var outputStringBufferSize: Integer; var errorMsgBuffer: PChar; var errorMsgBufferSize: 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);

转换字符串被调用一次以获取缓冲区大小,然后再次调用以填充缓冲区。在第二次通话中,我注意到了不同之处。当使用来自 Test 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);    
4

1 回答 1

2

1.9 不能用浮点数精确表示,而 225 可以精确表示。

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

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

于 2012-06-06T22:15:21.403 回答