fredag, oktober 10, 2008

FPU issues when interoping Delphi and .net

In a project recently, we needed to do some interop between Codegear Delphi and MS .Net, that is, we have .net code that needed to be wrapped as an COM object so it can be called from native code or Delphi.

The developer of the .net library created an C++ client to test the library and everything working fine.
Then we develop the Delphi client, and after some googling etc we are good to go. Here are some articles describing the details:

http://interop.managed-vcl.com/netinterop_csharp.php
http://www.drbob42.com/examines/examin36.htm
http://www.blong.com/Conferences/BorCon2004/Interop2/COMNetInterop.htm#CCW

But then... one method throws an exception:

Project TestClient.exe raised exception class EOleException with message 'Overflow or underflow in the arithmetic operation'.

What is this?, the method are doing some serious calculations, but it works fine from C++, and from other .net clientes that have been usiing the library for years, newer a hitch, so what gives?
I did not VisualStudio on the computer where I run Delphi, so I could not debug it in a proper fashion, and if I had, I guess I would struggle a bit to get it working, but anyway, I started to pepper the code in the library with some loggingstatement, to see what was happening, and surely, I found one point in the code that throwed the exception.
It was just one problem.. We do not get any exception at that point in the code...

Boiled down to basics, this is the code in C#:

double x = Double.NaN;
bool b = Double.IsNaN(x);

At this point, it's the IsNaN that throws, so modified a bit:

double x = Double.NaN;
bool b = (x==2.0);

And now it is the x==2.0 that throws??
Then I try:

double x = 3.2;
bool b = (x==2.0);

This worked..., and this:

double x = 3.2;
bool b = Double.IsNaN(x);

that worked to..... hm.. firing up Lutz Roeder's Reflector (yes, I know, it's Red-Gate now, but for me, it's always Lutz's Reflector..), and searching for the Double.NaN I find:

public const double NaN = (double) 1.0 / (double) 0.0;

WHAT?, but, isn't 1/0 an divide by zero?, so why does this work in C# plain, but not when called from Delphi?
At this point I remembered some years ago, when Delphi was the tool of choise.. there have always been some funky stuff going on in the RTL package of Delphi, but what could this be..

I started to toss some ideas to a friend of me, mashi, who is an serious bitfiddler, out of the blue,
he just asked me:

"maybe you need to flip a bit or two int the FPU's control register"

I was stoked.. the FPU??, why is that?, and then he could tell me that some DirectX libraries also changes the FPU settings to give more juice when doing some calculations..

Whoha, that's funny, but after looking at this article:

http://webster.cs.ucr.edu/AoA/Windows/HTML/RealArithmetic.html

I find that there are 6 bits in the FPU's control register that actually controls exception handling by the FPU..

image (It’s the yellow one’s whe are after..)

So, to confirm, we need to find the current state, and luckily both Delphi and Visual Studio have debug-windows, that show us the value of the CTRL register, and they have very different values, in Delphi it’s 1272, and in C# it’s 027F (both number is hex), so, if we look at them as binary numbers:

.Net   027F = 0000 0010 0111 1111
Delphi 1272 = 0001 0010 0111 0010



For us, bit number 2 (it's zerobased so for you nongeeks (which lost this before reading this far anyway’s) it's bit 3 from the right), this bit is set to 1 in .net, and zero in Delphi..




After some more googling, I found this:


http://www.stats.uwo.ca/faculty/murdoch/software/compilingDLLs/pascal.html


Which also say something abut this under the title Preserving Registers:

The only problem that is likely to arise is with the floating point processor (FPU) registers. Some versions of Delphi change the FPU control word upon entry to a DLL (but this is not true of Delphi 5);





Yeah, I know, the R thingy that that article is about, is probably some other thing that is far away from thiw, but it told me what I needed to hear. :)




So, armed with knowledge, I wanted to change the CTRL registers to see what would happen, and what do you know, even MS have an Q article about this very problem, but in an little bit different context:




PRB: System.Arithmetic Exception Error When You Change the Floating-Point Control Register in a Managed Application

http://support.microsoft.com/kb/326219




So, after implementing this simple line in C#:


_controlfp(_CW_DEFAULT, 0xfffff);

everything works..


What we still don't know though.. will this have any sideeffects?, I guess time will tell..





I'm guess we should save the currect CTRL word, and restore it when we return from our method, but I'm not yet sure if Delphi also does it, so we need to do some test before we conclude, but at least, the NaN code is not giving us any problems now.

onsdag, oktober 08, 2008

I guess you could call this an developers wet dream...

Or something.. :D

So, I got this room filled with computers, geeks needs something to give credit for their geekness :), so atleast 4-5 is needed, but this is noisy, costly in terms of power consumption, and today we want to be a little greener than yesterday.. (ah, well, let’s see about that)

So, after some dealing with my boss, I got this nice home-computer, so I can do some serious work from home, atleast, it’s the plan :)

Dell offered me this thingy, called T7400, which I can say, it’s a pure monster. The weight alone was about to kill me, carrying this thingy 4 floors..

Some geeky details:
- 2 x Xeon E5440, 4 core, 12MB cache, 2.84Ghz
- 8 x 2 GB Fully Buffered RAM (with 8 sockets to spare)
- 2 x 750 GB SATA’s in RAID (stripe of course, who’s scared?)
- Nvidia Quadro FX 570 gfx card
- Powerhouse of an PSU, 1KW.. (the not so green thing)
- And ofcourse, and Dell 24” monitor, wish it was 30”.. (Sponsors??)

Later adjusted a bit, an extra 1TB harddrive, and an 150GB 10RPM RaptorX drive as an system drive.
All my drive, except the Raptor is 7200RPM, but my computer is quiet as, uhm, an bee… :)

Some pics:


Some memory, this is like 16GB of memory, Fully Buffered kind, wish they choosed some cheaper drugz, I need more soon:), however, I have 8 slots available…


And a few cores, conveniently wrapped in lots of four in each tower, the towers are hiding my Xeon E5440, 2.83Ghz, 4core, It’s not the worst computing towers money can buy, but they will suffice fine..
 
 
Even developers need some gfx, As an dev, I hardly can justify hardcore shit, I even don’t spend time with games, so I only got this Quadro FX570, but it seems to be good enough for me: