View Single Post
  #42   Report Post  
Posted to microsoft.public.excel,microsoft.public.excel.programming
Martin Brown Martin Brown is offline
external usenet poster
 
Posts: 230
Default Excel and the Math Coprocessor for DLLs

On 03/04/2012 23:50, Lynn McGuire wrote:
On 4/2/2012 7:33 PM, joeu2004 wrote:
"Lynn McGuire" wrote:
TestDLL.dll is created using my testdll.c code and
the Open Watcom C compiler and linker.



Alternatively, as I explained previously, you can call a DLL routine
directly from the VBA procedure CommandButton1_Click. The DLL
routine would call _control87(_PC53 + _RCNEAR, _MCW_PC + _MCW_RC).

That would obviate the need to call _control87 from each DLL routine
that you might call from CommandButton1_Click.

But you would need to call that DLL routine from every VBA procedure
that you initiate, either with an Excel "button" or by calling a
VBA function from an Excel formula.

So arguably, it is more reliable to call _control87 (or a DLL routine)
from each entry point that allow to be called from VBA.


Done. No better calcs.


OK. There is something odd here. The link map for your DLL suggests that
the code includes the FPU emulator emu387.lib code as well (which AFAIR
was obsoleted more than a decade ago). It is entirely possible that the
387 emulator cuts enough corners that it cannot compute the FPU test sum
right even when asked to do so.

Incidentally your compiler has optimised *at compile time* the original
division that you think you are testing to a multiplication by the
reciprocal. It is a technique called strength reduction.

That is (X/Y)*Y - X was compiled as X * (1/Y) * Y - X
where the value (1/Y) was computed by the compiler and not by the FPU!
(almost any modern optimising compiler will do this because at runtime
an FP division is significantly more expensive than a multiply)

MS Visual C goes further an spots that the result is identically zero.
Optimiser on it compiles to FLDZ.

What you are seeing here could well be a defect in how the *compiler*
optimises expressions since it will quite likely use a 64bit mantissa
here. I now think that is the root cause of your "problem".

The Math Coprocessor status is 0x4020 and control is 0x137f
The Math Coprocessor status is is now 0x127f
The Math Coprocessor status is 0x4000 and control is 0x127f
The Math Coprocessor status is is now 0x127f
The Math Coprocessor status is 0x4000 and control is 0x127f
The Math Coprocessor status is is now 0x127f


OK. The math coprocessor is now in the right state. With this change
does your fluid mechanics code now give the right expected answers?

I call the following code from each entry point.

unsigned checkMathCoprocessorStatus ()
{
unsigned old87Status = 0;
unsigned old87Control = 0;
unsigned new87result = 0;

old87Status = _status87 ();
old87Control = _control87 (0, 0);
//if (old87Status != 0)
{
char msg [4096];
sprintf (msg, "\nThe Math Coprocessor status is 0x%x and control is
0x%x\n\n", old87Status, old87Control);
writeLineToScreen (msg);
// new87result = _control87 (_PC_64 + _RC_NEAR, _MCW_PC + _MCW_RC);
// new87result = _control87 (_PC_53, _MCW_PC);
// new87result = _control87 (_PC_64, _MCW_PC);
// new87result = _control87 (_RC_NEAR, _MCW_RC);
// _asm { FINIT };
//new87result = _control87 (0x27f, 0xffff);
//new87result = _control87 (0, 0);
new87result = _control87 (_PC_53 + _RC_NEAR, _MCW_PC + _MCW_RC);
sprintf (msg, "\nThe Math Coprocessor status is is now 0x%x\n\n",
new87result);
writeLineToScreen (msg);
}

return old87Status;
}

eg

void checkMathCoprocessorStatus (unsigned &old87Status, unsigned
&old87Control)
{
old87Status = _status87 ();
old87Control = _control87 (0, 0);
}

unsigned setMathCoprocessorStatus (unsigned value, unsigned mask)
{
unsigned old87Control = _control87 (value, mask);
return old87Control
}

To be able to help further strip out every last thing that isn't
absolutely essential from the code and make all routines individually
callable from VBA. Also disable all compiler optimisations and tell it
to assume an FPU is fitted (this alone may fix your problem once the
right rounding mode is enabled and that seems to have worked OK).

Also make the value "Y" used in the (X/Y)*Y-X expression parameters to
the calling routine - that way the compiler cannot pre compute (1/Y)

--
Regards,
Martin Brown