FPU
Control & Exception Handling
See Also:� Error Handling
The FPU -- Floating Point Unit -- has configurable settings that allow you to control the precision of calculations, the rounding mode, and exception handling.� These settings are defined as part of the IEEE 754 standard, and are implemented in today's computers at the hardware level by various PC chipmakers.� This help file topic is mainly concerned with explaining how to implement these settings from uCalc Fast Math Parser, and not so much with why or when to use them.� For a detailed explanation of the FPU, see the IA-32 Intel Architecture Software Developer�s Manual, which is available at www.intel.com.� Also consult documentation for the IEEE 754 standard.� Visit www.ieee.org to find out more.
uCalc Fast Math Parser keeps two separate FPU control word settings, which are insulated one from the other.� One is for uCalc Fast Math Parser, and the other is for your host program.� If you have another DLL component that uses a different FPU setting, this will not affect uCalc Fast Math Parser or vice-versa.� Things run most efficiently if uCalc's FPU word is the same as that of your host program.
If you mask a particular exception, then instead of raising an error when this exception is encountered, a special value will be returned.� For instance, if the division by 0 exception is masked (with uc_FPU_Mask_ZeroDivide), then 1/0 would return Inf (for Infinity) instead of raising a Divison by 0 error.
Note: Managed code under Visual Studio 2012 does not work well with unmasked settings.� Therefore, it is best to leave the FPU control word with the default settings in C#, VC++, and VB.NET.
Here is the list of uCalc FPU commands:
uc_SetFPU
uc_SetFPU_HostProg
uc_GetFPU
uc_GetFPU_HostProg
uc_ToggleFPU
uc_ToggleFPU_HostProg
Here is the list of FPU Control word settings:
uc_FPU_Mask_InvalidOp
uc_FPU_Mask_DenormalOp
uc_FPU_Mask_ZeroDivide
uc_FPU_Mask_Overflow
uc_FPU_Mask_Underflow
uc_FPU_Mask_PrecisionLoss
uc_FPU_Precision_Single
uc_FPU_Precision_Double
uc_FPU_Precision_Extended
uc_FPU_Round_Even
uc_FPU_Round_Down
uc_FPU_Round_Up
uc_FPU_Round_Toward
If you wanted to toggle the Division by 0 bit for instance, you'd do it like this (in VB):
ucFPU(uc_ToggleFPU, uc_FPU_Mask_ZeroDivide)
where the first argument is a command, the second argument is always 0, and the third argument is the particular setting you want to toggle.� If the ZeroDivide mask was off, uc_ToggleFPU would turn it on.� If it was on, uc_ToggleFPU would turn it off.� If you wanted to set multiple bits instead of toggling just one, then you would add them together, like this (using uc_SetFPU instead of uc_ToggleFPU):
ucFPU(ucSetFPU, uc_FPU_Mask_PrecisionLoss + uc_FPU_Precision_Extended
+ uc_FPU_Round_Even)
The default start-up FPU setting is arbitrarily configured as follows:
uc_FPU_Mask_InvalidOp
+ uc_FPU_Mask_DenormalOp
+ uc_FPU_Mask_ZeroDivide
+ uc_FPU_Mask_Overflow
+ uc_FPU_Mask_Underflow
+ uc_FPU_Mask_PrecisionLoss
+ uc_FPU_Precision_Extended
The following three Visual Basic examples assume that you are starting with the default setting.� If you have a different setting, you can start each example with the following line:
ucFPU(ucSetFPU, uc_FPU_Mask_InvalidOp _
+ uc_FPU_Mask_DenormalOp _
+ uc_FPU_Mask_ZeroDivide +
uc_FPU_Mask_Overflow _
+ uc_FPU_Mask_Underflow + uc_FPU_Mask_PrecisionLoss _
+ uc_FPU_Precision_Extended)
The demo files for the various supported compilers contain source code examples for toggling the FPU in those programming languages.
Example 1:� Division by 0 vs Inf
Visual Basic
Print ucEvalStr("1/0")
' This returns Inf ucFPU(uc_ToggleFPU, uc_FPU_Mask_ZeroDivide) Print ucEvalStr("1/0")
' This returns a Division by 0 error ucFPU(uc_ToggleFPU, uc_FPU_Mask_ZeroDivide) Print ucEvalStr("1/0")
' This returns Inf again |
Visual Basic
Print ucEvalStr("100!^150")
' This returns Inf ucFPU(uc_ToggleFPU, uc_FPU_Mask_Overflow) Print ucEvalStr("100!^150")
' This returns a Floating point overflow error ucFPU(uc_ToggleFPU, uc_FPU_Mask_Overflow) Print ucEvalStr("100!^150")
' This returns Inf again |
Visual Basic
Print ucEvalStr("Sqr(-5)") ' This returns NaN ucFPU(uc_ToggleFPU, uc_FPU_Mask_InvalidOp) Print ucEvalStr("Sqr(-5)") ' This returns an Invalid operation
error ucFPU(uc_ToggleFPU, uc_FPU_Mask_InvalidOp) Print ucEvalStr("Sqr(-5)") ' This returns NaN
again |
Example 4:� Allowing your compiler to catch an exception
In this example, a uCalc error handler is defined, which returns ucReRaise in order to allow the host Delphi compiler to catch the exception, using try / except.� If you enter an expression such as 1/0, and the Division by 0 FPU setting is not masked, then a message box will display:� This is handled by Try/Except in Delphi.
Delphi
function MyErrorHandlerB(t:
Longword): Longword; stdcall; begin ��
// Additional code may go here if necessary �� Result :=
ucReRaise; end; procedure TForm1.btnEvalClick(Sender: TObject); begin �� try ����� txtResult.Text := ucEvalStr(txtExpression.Text); �� except ����� ShowMessage('This is handled by Try/Except in Delphi'); �� end; end; // The following line can go in
TForm1.FormCreate() uCalc(uc_AddErrorHandler, 0, Longword(@MyErrorHandlerB)); |
What�s different in
version 3.0
� Instead of setting the FPU using the general uCalc() function, you should now use the provided ucFPU() function.� Also, it now takes two arguments instead of three (the second argument was unused).