简体   繁体   English

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

[英]Calling Delphi 6 DLL from a C# results in different rounding?

I am having some serious issues with delphi rounding (when used from ac# project) and consistency of it's results when the value seems to fall within the .5 bracket ie is it rounding up or down??? 我有一些严重的delphi舍入问题(当从ac#项目使用时)和它的结果的一致性,当值似乎落在.5括号内时,即它是向上还是向下舍入???

I have created a delphi DLL that performs a number of operations. 我创建了一个执行许多操作的delphi DLL。 The crux of one of the operations is (as an example) 其中一项业务的关键是(例如)

double := Round(double1   * double2);

where double1 = 1.9 and double2 = 225 (values obtained from file these are read from) 其中double1 = 1.9和double2 = 225(从文件中获取的值从中读取)

The result on my calculator says it should be 427.5. 我的计算器上的结果表明它应该是427.5。 The result in the delphi DLL says 427. Rounding down, all good as per delphi documentation for Round() delphi DLL中的结果显示为427.四舍五入, 圆形()的 delphi文档都很好

The values of these calculations are stored against Delphi (classes I believe) which represent forms. 这些计算的值存储在表示形式的Delphi(我相信的类)中。 In the case above like so 在上面这样的情况下

property SRDairy           : Double   read FSRDairy             write FSRDairy;

This property belongs to a bunch of classes and is serialized to the buffer in the Delphi DLL like so (AOvrFarm is the parent that contains all the items to be serialized included my SRDairy one) 这个属性属于一堆类,并且像这样被序列化到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;

There are a-lot of other properties, forms (classes) etc that are done in this DLL. 在这个DLL中有许多其他属性,表单(类)等。 Upon the end, this file is serialised to a string and that string is returned to the caller (c#) app and the refoutputStringBuffer is filled using something like (in delphi): 结束时,此文件被序列化为一个字符串,该字符串返回给调用者(c#)应用程序,refoutputStringBuffer使用类似(在delphi中)填充:

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

My issue is when I'm calling this DLL from ac# application. 我的问题是当我从ac#应用程序调用此DLL时。 The result I'm getting is not 427, it's 428. Rounded up!!!! 我得到的结果不是427,它是428.圆满了!!!!

The Delphi signature: Delphi签名:

function ConvertString(fileContents: PChar; fileExt: PChar; var outputStringBuffer: PChar; var outputStringBufferSize: Integer; var errorMsgBuffer: PChar; var errorMsgBufferSize: Integer): WordBool; function ConvertString(fileContents:PChar; fileExt:PChar; var outputStringBuffer:PChar; var outputStringBufferSize:Integer; var errorMsgBuffer:PChar; var errorMsgBufferSize:Integer):WordBool; stdcall; STDCALL; export; 出口;

I use the DLL from my c# like so: 我使用我的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);

The convert string is called once to get the buffer size and then again to fill the buffer. 转换字符串被调用一次以获取缓冲区大小,然后再次调用填充缓冲区。 It's on the second call that I notice the difference. 这是第二次电话,我注意到了差异。 When using the same DLL from a Test Delphi project I get 427 (rather than the c# 428). 当从Test Delphi项目使用相同的DLL时,我得到427(而不是c#428)。

I've tried building my c# against Any CPU and just x86 incase it was some sort of cpu 64bit issue as my PC is a 64bit machine. 我已经尝试构建我的c#对任何CPU和x86只是因为我的PC是64位机器,它是某种cpu 64位问题。

Has anyone ever come across this kind of thing, and if so did they have any way around it? 有没有人遇到过这种事情,如果有的话,他们有什么办法吗?

EDIT - ANSWER 编辑 - 答案

As mentioned by David Heffernan the control word used when calling the DLL from c# was different to that used when calling from another Delphi application. 正如David Heffernan所提​​到的,从c#调用DLL时使用的控制字与从另一个Delphi应用程序调用时使用的控制字不同。 The control word used when calling from c# was in fact $639. 从c#调用时使用的控制字实际上是639美元。

Unfortunately, setting the control word to $1372 caused issues in my c# app while debugging namely it caused the local's watch window to show "unable to evaluate due to stack overflow state" exceptions for all variables. 不幸的是,将控制字设置为$ 1372导致我的c#app在调试时出现问题,即它导致本地监视窗口显示“由于堆栈溢出状态无法评估”所有变量的异常。 I found an article - strange floating point results which talks about this and hence changed it to be $133F instead (which resolved both the floating point differences and the c# debugging issues). 我发现了一篇文章 - 奇怪的浮点结果 ,它谈到了这一点,因此改为$ 133F(它解决了浮点差异和c#调试问题)。

Code being: 代码是:

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

1.9 is not exactly representable in floating point, whereas 225 is exactly representable. 1.9在浮点中不能完全表示,而225是完全可表示的。

It's not clear where the input numbers are coming from, but it is simply impossible with IEEE754 arithmetic to perform the product 1.9*225 exactly. 目前尚不清楚输入数字的来源,但IEEE754算法根本不可能完全执行产品1.9 * 225。 The behaviour you see has nothing to do with rounding and is in fact all about representability. 您看到的行为与舍入无关,实际上是关于可表示性的。 If you need to perform this calculation exactly then you need to use decimal arithmetic rather than floating point. 如果您需要完全执行此计算,则需要使用十进制算术而不是浮点。 That would mean using the currency type in Delphi or decimal in C#. 这意味着在Delphi中使用货币类型或在C#中使用小数。

The source of your differing behaviour is presumably down to the 8087 control word that controls the floating point register. 您的不同行为的来源可能低至控制浮点寄存器的8087控制字。 When your DLL executes, called from C#, the control word will differ from the default Delphi setting. 当您的DLL执行时,从C#调用,控制字将与默认的Delphi设置不同。 Call Set8087CW($1372) at the entry point to your DLL to use the default Delphi control word. 在DLL的入口点调用Set8087CW($ 1372)以使用默认的Delphi控制字。 Remember to restore it before you return from the DLL. 请记住在从DLL返回之前恢复它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM