Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/RTL/Delphi/Other RTL    [ Add a report in this area ]  
Report #:  5928   Status: Closed
System Initialization Errors Affecting the FPU Control Word
Project:  Delphi Build #:  4.453
Version:    7.0 Submitted By:   John Herbster
Report Type:  Minor failure / Design problem Date Reported:  9/11/2003 5:05:29 PM
Severity:    Infrequently encountered problem Last Updated: 5/7/2004 2:43:22 PM
Platform:    All versions Internal Tracking #:  
Resolution: Test Case Error (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: (9 Total Ratings)
4.89 out of 5
Total Votes: 1
Description

There is a problem with FPU (Floating Processor Unit) Control Word being initialized to different precisions on different computers.  The problem comes up every now and then in the forums.  I am sure that many of us have been bothered by such symptoms of the problem, such as occasional changes in rounding, without ever knowing that the precision changes were the reason.  

On one of my machines, with a Delphi 7 program (T_CkFPU_1) using Forms on a Win2K SP4 machine, the FPU control word precision is set to $1272 which is for double precision, instead of the correct extended precision when my code gets control.  With the same program, on a machine with WinXP SP1, the control word is correctly set to $1372 for a precision of extended.  

(The 2nd digit of the CW from the left indicates the precision.  "2" is double; "3" is extended.)

On both the Win2K and the WinXP machines, the initial value of the System unit s Default8087CW variable is still set correctly set to extended precision ($1332).

The problem does not seem to happen with my console mode program (T_CkFPU_2) under either Win2K or WinXP.

I tracked the problem down to a two pieces of, what I call, bad code
(1) in D7's Forms.pas; and the other in
(2) Windows.pas.

In Forms.pas, procedure TApplication.CreateHandle has the code
    ...
    FHandle := CreateWindow(...);
    ...
    if NewStyleControls then
    begin
      SendMessage(FHandle, WM_SETICON, 1, GetIconHandle);
      SetClassLong(FHandle, GCL_HICON, GetIconHandle);
    end;
    ...
where SendMessage is defined in User32.dll.  This SendMessage on some machines or under some conditions can change the FPU's control word.  (In my case from extended to double precision.)

In Windows.pas, function CreateWindow(...) and
similarly CreateWindowEx(...) have the code
  begin
    FPUCW := Get8087CW;
    Result := _CreateWindowEx(...);
    Set8087CW(FPUCW);
  end;

Notice that they read FPUCW from the actual FPU's CW instead of the Default8087CW (which can be different) and that on exit the Set8087CW() sets both the actual FPU's CW and the Default8087CW.  (This is why we try to avoid having redundant variables in our programs.)

If the CreateWindows and CreateWindowsEx code is going to read the actual FPU's CW on entry, then it should only restore FPU's CW on exit and *not* also set the Default8087CW variable.  Or optionally, if on entry it reads the Default8087CW, then it should restore the Default8087CW on exit.

In Forms.pas, procedure TApplication.CreateHandle, I think that the problem could also be fixed by adding restoration of the FPU's CW in the return from calling the User32.dll SendMessage procedure.

I am attaching the Delphi source for both the program using forms (T_CkFPU_1) and the console mode program (T_CkFPU_2).

Fixing this problem will eliminate some rare and hard to track down user problems.  

--JohnH

Steps to Reproduce:

I am attaching the source for two programs to test for the problem or your machines


T_CkFPU_2 is a Windows console mode program which will indicate the values of the System variable Default8087CW and the state of the FPU control word when the progammer code gets control.  A display of either of the following lines means OK.

     Default8087CW/FpuCW=$1332/$1372
     Default8087CW/FpuCW=$1332/$1332


T_CkFPU_1 is a Windows forms mode program with some buttons and a memo box for the output.

The "initialization" line here, is from the WinXP system and is correct:

    At initialization: Default8087CW/FpuCW=$1372/$1372

The following initialization line from a Win2K machine is incorrect:

    At initialization: Default8087CW/FpuCW=$1332/$1272

As mentioned in the description: The 2nd digit of the CW from the left indicates the precision.  "2" is double; "3" is extended.


There is an additional file, T_CkFPU_1_Log.txt, included.  It is an annotated log file of notes that I developed while tracing through the initialization to find the source of problems.
Workarounds
Somewhere in initialization of your programs before the FPU is used for critical operations, insert the statement "Set8087CW($1332)".  I suggest that it be added immediately after the "begin" in the DPR file, like the following:

  ...
begin
  Set8087CW($1332);
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Attachment
T_CkFpu 030911b.zip
Comments

John Herbster at 9/12/2003 3:48:08 PM -

The easiest fix might(?) be to drop the following line in the hidden code that executes just before the unit initialization sections.

  Set8087CW($1332);

Is it possible that code that is outside of the application programmer's control that modifies and doesn't restore the FPU CW is executed after that point?

Danny Thorpe at 9/14/2003 9:59:34 PM -
The Delphi RTL already sets the FPU control word to $1332 at program startup.  See _FPUInit in System.pas.

The FPU control word is being changed by a DLL load during program startup somewhere else in your program.

John Herbster at 9/18/2003 3:28:02 PM -
Yes , the FPU control word is being changed. I hope by "in your program" you did not mean the part of the program that I wrote.  As I tried to mention in the description, it is being changed by the call to "SendMessage(FHandle, WM_SETICON, 1, GetIconHandle)" from inside the Forms.PAS unit.

Danny Thorpe at 9/21/2003 10:56:24 PM -
A DLL is being loaded into your program at runtime.  It may be part of the OS , or part of some third party system-wide utility you have installed on your system (such as virus scanners or desktop managers).

I'm familiar with the hazards of DLLs changing the execution environment.  That's why I added the SafeLoadLibrary routine in SysUtils.

My point is that a DLL is being loaded into your process at runtime, and not by request of the RTL or VCL.  

I hope you're not proposing that every call to an external function be codegen'd to save and restore the processor state....  The peformance of that would be awful.

John Herbster at 5/7/2004 1:14:24 PM -

Danny, OK I change the proposal to *automatically* add a "Set8087CW($1332);" line to the DPR code right after the "begin".

This will solve a lot of your customers a lot of grief.

*Without* that line, I get the following results:
  Fix status: Not fixed
  Low(GetVersion)=0, NewStyleControls=true
  At initialization: Default8087CW/FpuCW=$1372/$1272
  After DPR's begin: Default8087CW/FpuCW=$1372/$1272
  At FormCreate:     Default8087CW/FpuCW=$1272/$1272
Note that the FPU precision changed from extended to double, in between the time that the form's initialization section ran and the time that the FormCreate ran.

*With* that line, I get the following results:
  Fix status: Fixed with Set8087CW after DPR's "begin"
  Low(GetVersion)=0, NewStyleControls=true
  At initialization: Default8087CW/FpuCW=$1372/$1272
  After DPR's begin: Default8087CW/FpuCW=$1332/$1372
  At FormCreate:     Default8087CW/FpuCW=$1372/$1372
Note that the precision was partly changed from extended to double, in between the time that the form's initialization section ran and the time that the DPR code ran, but that the Set8087CW($1332) in the DPR fixed it.

Regards, JohnH

John Herbster at 9/22/2003 7:16:47 AM -

The FPU CW precision is being changed before my program gets control.  As I tried to say in this report's description, the FPU CW precision is being changed during a "SendMessage(FHandle,WM_SETICON,1,GetIconHandle)" call.

This happens *before* any application code is executed.

What I think might work to fix the problem, is to add proper save and restore of the Default8087CW and FPU CW around the code in Window.PAS and maybe also Forms.PAS mentioned in the description.  (Notice the word "proper".  Because there are two places where the FPU's CW is stored, it requires some care.)

John Herbster at 3/30/2004 4:39:13 PM -

> A DLL is being loaded into your program at runtime.  

True.  As I mentioned in the Description, it appears to be User32.DLL that is loaded by the call to SendMessage in the Forms.pas unit. There is bad code in the Forms unit that does not preserve the state of the FPU's control word.

> My point is that a DLL is being loaded into your process at runtime, and not by request of the RTL or VCL.  

It is happening before the "begin" in the DPR file.

I am only asking for the code in Forms and Window.pas to be fixed, or, much simpler, that a single call Set8087CW($1332) be done just before the first line of user initialization code is executed.

John Herbster at 5/8/2004 6:05:36 AM -

Danny,  

I respectfully suggest that you are mistaken here when you say "My point is that a DLL is being loaded into your process at runtime, and not by request of the RTL or VCL."

The two areas of faulty programming that can and do allow this to happen are mentioned in my original description of the problem.

A quick and dirty fix is just to hide a Set8087CW($1332) call in the RTL code just before the jump into the first line of application code.

As I mentioned already, the user work-a-round is to put the Set8087CW($1332) call just after the "begin" in the user's DPR file.

John Herbster at 6/28/2004 7:21:23 PM -

I have created a new report (QC#8399) on the same subject w/o any user test code (in order to avoid the easy resolution of "Test Case Error") in hopes of getting this problem fixed.  --JohnH

Server Response from: ETNACODE01