I am having some serious issues with delphi rounding (when used from a c# project) and consistency of it's results when the value seems to fall within the .5 bracket i.e. is it rounding up or down???
I have created a delphi DLL that performs a number of operations. 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)
The result on my calculator says it should be 427.5. The result in the delphi DLL says 427. Rounding down, all good as per delphi documentation for Round()
The values of these calculations are stored against Delphi (classes I believe) which represent forms. 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)
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. 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):
StrLCopy(outputStringBuffer, PChar(resultString), bufLen - 1);
My issue is when I'm calling this DLL from a c# application. The result I'm getting is not 427, it's 428. Rounded up!!!!
The Delphi signature:
function ConvertString(fileContents: PChar; fileExt: PChar; var outputStringBuffer: PChar; var outputStringBufferSize: Integer; var errorMsgBuffer: PChar; var errorMsgBufferSize: Integer): WordBool; stdcall; export;
I use the DLL from my c# like so:
[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).
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.
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. The control word used when calling from c# was in fact $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. 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).
Code being:
begin
cw := Get8087CW;
Set8087CW($133F);
....
finally
Set8087CW(cw);
1.9 is not exactly representable in floating point, whereas 225 is exactly representable.
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. 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#.
The source of your differing behaviour is presumably down to the 8087 control word that controls the floating point register. When your DLL executes, called from C#, the control word will differ from the default Delphi setting. Call Set8087CW($1372) at the entry point to your DLL to use the default Delphi control word. Remember to restore it before you return from the DLL.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With