I just discovered that I’m getting FPU zerodivide exceptions I’m not responsible for because I’m calling trunc, which resets the masking off of zerodivide errors done in fmod.pas, and causes an earlier error to surface.
I can program around that, but I’m concerned: having reenabled zerodivide interrupts, might not subsequent fmod calls fail for floating-point errors?
- Elly asked 16 years ago
The FPU exceptions are masked because Microsoft in all their infinite wisdom decided that FPU exceptions should be masked by default, so the Visual C++ startup code sets the mask. Borland do not mask FPU exceptions except in specific functions such as Trunc. This means that any libraries developed in Visual C++ that do a lot of math operations usually throw a few FPU exceptions when used with Borland compilers. The developers of the libraries do not see these exceptions because they are always masked in Visual C++. Just try using Direct3D or OpenGL in Borland without masking FPU exceptions. You will eventually get an FPU exception that never occurs in Visual C++. The same goes for FMOD. Unfortunately, calling Trunc seems to reset the FPU exception mask.
You can override System.Trunc easily by specifying your unit name as a qualifier in the call to Trunc. Say your own Trunc functions were in a unit called MyFuncs. You could call Trunc by using the syntax MyFuncs.Trunc. This way you do not need to change the uses clause of any units to a specific order.
If you manage to get a hold of “Game Programming Gems 3”, there is a section in there about FPU exceptions, compilers that mask FPU exceptions, and why some FPU exceptions should always be unmasked to assist with producing more robust and faster code.
This is an annoying issue, and in addition to “Trunc”, “Int” and “Frac” also reset the control word and unmask exceptions.
Borland somehow implemented a workaround in Delphi 6+, by saving, masking and restoring the control word, which is an “exact” fix, but also a very slow one you don’t want to use anywhere time critical.
If you can, use Round instead of Trunc, most of the time Round(x-0.5)=Trunc(x), and Round(x-0.5) is usually faster.
I’m having similar issues. My program generates floating point errors on a friends machine.
I’m including the GLScene Geometry unit. I have stepped through the calls to make sure the Geometry unit is being used instead of the system unit.
It still generates exceptions. It’s a real pain. The only FMOD that seems to work on that machine is an ancient version doesn’t have EAX support. Unfortunately that FMOD doesn’t have Ogg support either.
I’m assuming the card in question has dodgy EAX support.
I’m using Delphi 5, does anyone know if upping the version to 6 or 7 will help (given that using glScenes Geomety unit didn’t).
and (talking to Brett now) Any chance of a EAX-less version as a standard release option? Or at the very least a EAX-less release of the current version to check if it is the problem. I’m hoping it can be removed with just a #define change.
- Lerc answered 16 years ago
These may help. They are from [url=http://glscene.org/:2nlyrkfg]GLScene[/url:2nlyrkfg]
[code:2nlyrkfg]// Trunc64 (extended)
function Trunc64(v : Extended) : Int64; register;
FISTP qword ptr [ESP+4]
// Trunc (single)
function Trunc(v : Single) : Integer; register;
FISTP dword ptr [ESP+4]
Unfortunately, I’ve never used assembler, and haven’t a clue how. I got this error message when trying to compile:
"Undeclared identifier: cwChop"
It’s the only compile error I got (once per routine). Is it defined in the code you copied this from, or somewhere in a standard Delphi unit?
I’m using D5.
Thanks again. I tried the two functions yesterday, combined under the single name “trunc” as overloads.
Then I realized that to have them supersede System.trunc, I’ll have to put them into a separate unit and change the “uses” directives of each unit that calls “trunc” to put this unit in front of System. And it was awfully late at night. So the effort is on hold for the moment.
I’ve been meaning to raise this question: Why are the interrupts disabled in the first place? What are the consequences if I don’t disable them?
I make all my fmod calls from a single unit, and it might be easier just to mask the interrupts before making calls that need it, and unmask them afterward, catching any that are raised as a result of the unmasking. This would work as long as the interrupts aren’t asynchronous with my thread.
Please login first to submit.