Neslib is a (still small) library of Delphi utilities that is shared across some other Neslib projects.
Neslib is licensed under the Simplified BSD License.
See License.txt for details.
High-Precision Floating-Point Types for Delphi
License: Other
As reported by @Andreas113
Hi Erik,
In the meantime, I made several more numerical tests of the mathematical routines and I came across the following error:
Function: Hyperbolic tangent: Tanh(..)
Numerical Example:
X = 3.42897556698888888812345
Y = +1.63128799224455996622337700123
MPA_Float = 0.442822429892976193018935445355621315339799605925608439366306296677512: Exact!
DoubleDouble = 0.4428224298929761930189354453556
QuadDouble = 0.44282242989297619301893544535562131533979960592560843936630630
Conclusion:
DobleDouble: -----> OK!
QuadDouble : -----> OK!
X = -3.5
Y = 1.0E-1
MPA_Float = -0.028563656570828037865016530810004960582783350513412052472238074033367: Exact!
DoubleDouble = -0.2781854903257024404718000872415 -----> WRONG!
QuadDouble = -0.27818549032570244047180008724146610677969449455154405945034634 -----> WRONG!
Conclusion:
DobleDouble: -----> WRONG!
QuadDouble : -----> WRONG!
On Delphi XE5 still only DoubleDouble runs, on Delphi 10.3 DobleDouble and QuadDouble.
The wrong result comes out - with this number combination - with both Delphi versions!
Even my old calculator (HP 41 CX) can calculate this value to 10 digits correctly...
So, there seems to be something wrong with the C routine. Or is it possibly due to the data transfer from Delphi to C?
Iโ will be keeping testing!
Regards,
Andreas
Hi Erik,
I'm sorry to tell you, but there are still errors in the parser routine:
Function: Division
Example:
Y/X:
----
X = -3.5
Y = -5.08888E+1
MPA_Float = 14.53965714285714285714285714285714285714285714285714285714285714285714 --> Correct
QuadDouble = 1.45396571428571428571428571428571428571428571428571428571428571 --> WRONG!
DoubleDouble = 1.453965714285714 --> WRONG!
The Exponent E+1 is not parsed correctly.
Andreas
Hi Erik,
Sorry to pester and harass you again, but I suffer from the nasty disease called perfectionism. My motto and guiding principle in programming is as follows: Make the software intelligent, because the user is stupid anyway.
So: let's keep improving the parser, if you like...
Function: Division
Example:
Y/X:
----
X = 1
Y = - 0
MPA_Float = 0 ---> Correct
QuadDouble = NAN
DoubleDouble = NAN
Y/X:
----
X = 1
Y = -2
MPA_Float = -2 ---> Correct
QuadDouble = NAN
DoubleDouble = NAN
Y/X:
----
X = + 1
Y = -2
MPA_Float = -2 ---> Correct
QuadDouble = NAN
DoubleDouble = NAN
Y/X:
----
X = 1
Y = - 2
MPA_Float = -2 ---> Correct
QuadDouble = NAN
DoubleDouble = NAN
Y/X:
----
X = 1
Y = - 2
MPA_Float = -2 ---> Correct
QuadDouble = NAN
DoubleDouble = NAN
The input strings are not parsed correctly. But the above cases can be solved very easily by trimming the inputs.
A bit more complex, but solvable is the following case:
Y/X:
----
X = 1E - 1
Y = - 2
MPA_Float = -20 ---> Correct
QuadDouble = -2.00000000000000000000000000000000000000000000000000000000000000
DoubleDouble = -2.0000000000000000000000000000000
Thanks
Andreas
As reported by @Andreas113:
Erik,
I just noticed something that may help you find the error:
It concerns both Delphi 10.3. and Delphi 10.3. XE5.
When assigning the values:
X_Dd:= '1E0';
X_Qd:= '1E0';
Y_Dd:= '3.5E0';
Y_Qd:= '3.5E0';
the program will crash. It is therefore probably due to your parser routine within Init.
Andreas
Hi Erik,
I have run numerous numerical tests over the past few days. I did not encounter any new parsing errors but I observed some incorrect results with overflow and underflow of input values in some mathematical functions like follows:
Function: hyperbolic cosine Cosh(..)
Example:
Cosh(Y*X):
----------
X = -4987654.6211234567891011
Y = -0.100000000000000009999995
MPA_Float = 6.122507439387169799822007412143668689493139250589157593313679373744026E+216610 --> Exact!
QuadDouble = NAN --> WRONG! --> Correct for DoubleDouble range: +Inf
DoubleDouble = NAN --> WRONG! --> Correct for QuadDouble range: +Inf
Function: hyperbolic sine Sinh(..)
Example:
Sinh(Y*X):
----------
X = -4987654.6211234567891011
Y = -0.100000000000000009999995
MPA_Float = 6.122507439387169799822007412143668689493139250589157593313679373744026E+216610 --> Exact!
QuadDouble = NAN --> WRONG! --> Correct for DoubleDouble range: +Inf
DoubleDouble = NAN --> WRONG! --> Correct for QuadDouble range: +Inf
Sinh(Y*X):
----------
X = -4987654.6211234567891011
Y = +0.100000000000000009999995
MPA_Float = -6.122507439387169799822007412143668689493139250589157593313679373744026E+216610
QuadDouble = NAN --> WRONG! --> Correct for DoubleDouble range: -Inf
DoubleDouble = NAN --> WRONG! --> Correct for QuadDouble range: -Inf
I cannot judge which result the original C functions return: It would therefore be possible that there is an error there in the C-code, but also in the implementation of the +/- infinity values in Delphi is possible.
I will be testing further and reporting to you.
Kind regards,
Andreas
Hi Erik,
have just discovered a small malfunction in the parser routine of QuadDouble.
Here is an example:
Procedure Print(Name: String; Value: QuadDouble);
VAR
S: String;
Begin
S:= QuadDouble.ToString(Value, TMPFloatFormat.Fixed);
WriteLn(Name + ' = ', S);
WriteLn;
End;{Print}
{---------}
VAR
ZQd: QuadDouble;
St : String;
. . .
St:= '12345.678901234567890123456789012345678901234567890123456789012345678901';
ZQd:= St;
Print('ZQd: Only the first 33 figures are correct: ', ZQd);
// ---> Result:
ZQd: Only the first 33 figures are correct: = 12345.67890123456789012345678901235113471715242452040893925930542385
--> 12345.67890123456789012345678901235113471715242452040893925930542385
Could you please have a look at it if you have time?
Thank you!
Regards,
Andreas
Great library, thanks for making it available for Delphi!
I was wondering how integers are handled. Extended/float80 can represent 64 bit integers without rounding, which makes it and ideal type to hold floats as well as integers.
Can DoubleDouble do this as well? Are integers guaranteed to be preserved if the operations don't involve fractions?
Is there a possibility to do precise division by an integer and truncating the fractional part?
Hi Erik,
if you are going to make changes to the code anyway, I have one more request for you.
For the Windows 32 target platform, it is relatively easy to include the use of the Extended type. I have already tried this and tested it several times: It works very well. This would make your library more compatible with the 10-byte "Extended world" instead of the less accurate 8-byte "Double world" of Windows.
For this you would only have to add the following additions to your library:
1):
function MultiPrecisionInit: UInt32;
Instead of:
. . .
SetPrecisionMode(pmDouble); // Original
New:
{IF Defined(WIN32)}
SetPrecisionMode(pmExtended);
{$ELSE}
SetPrecisionMode(pmDouble);
{$EndIF}
This would be necessary anyway, because with SetPrecisionMode(pmExtended); in Win32 e.g. the routine FloatToStrF(..) no longer works correctly and cuts off trailing decimal places.
2): In Interface:
Type
DoubleDouble = record
public
. . .
New:
class operator Implicit(const Value: Extended): DoubleDouble; inline; static;
class operator Implicit(const Value: DoubleDouble): Extended; inline; static;
3): In Implementation:
New:
class operator DoubleDouble.Implicit(const Value: Extended): DoubleDouble;
VAR
a, b: Double;
begin
// QuickTwoSum algorithm:
a:= Value;
b:= Value - a;
Result.X[0]:= a + b;
Result.X[1]:= b - (Result.X[0] - a);
end;
class operator DoubleDouble.Implicit(const Value: DoubleDouble): Extended;
Begin
Result:= Value.X[1] + Value.X[0];
End;
Thank you in advance!
Regards,
Andreas
As reported by @Andreas113:
I have used Delphi XE5 for the tests because I have not yet got my tested MPA float library working with Delphi 10.3 and there is no way to control the results. However, I have a simplified version of the tests without this MPA float library with Delphi 10.3.
Also, how are you able to use the code with XE5?
I call this routine at the beginning of the program to get the correct settings:
Procedure ConfiguraRegiaoBR;
// DecimalSeparator in SysUtils and System.SysUtils - Stack Overflow
// By Rodrigo Garcia
Var
WFormatoBR: TFormatSettings;
Begin
WFormatoBR:= TFormatSettings.Create;
WFormatoBR.DecimalSeparator := '.'; // ',';
WFormatoBR.ThousandSeparator:= ','; // '.';
WFormatoBR.CurrencyDecimals := 2;
WFormatoBR.DateSeparator := '/';
WFormatoBR.ShortDateFormat := 'dd/mm/yyyy';
WFormatoBR.LongDateFormat := 'dd/mm/yyyy';
WFormatoBR.TimeSeparator := ':';
WFormatoBR.TimeAMString := 'AM';
WFormatoBR.TimePMString := 'PM';
WFormatoBR.ShortTimeFormat := 'hh:nn';
WFormatoBR.LongTimeFormat := 'hh:nn:ss';
WFormatoBR.CurrencyString := 'R$';
System.SysUtils.FormatSettings:= WFormatoBR;
End;{Procedure ConfiguraRegiaoBR}
The DoubleDouble routines, so far I have tested, work with Delphi XE5, but not the QuadDouble's. I was able to locate the position in the debugger where the malfunction occurs.
The difference is here:
procedure QuadDouble.Init(const S: String; const FormatSettings: TFormatSettings);
. . .
if (C >= '0') and (C <= '9') then
begin
D := Ord(C) - Ord('0');
R := (R * 10) + D; // Line number: ca. 4017
Inc(ND);
end
else
begin
. . .
In the multiplication routine _qd_mul_qd_d(..) is the divergence:
D := Ord(C) - Ord('0');
// for example: If X_Qd:= '0.4'; --> S = '0.4';
// D = 0
// R = (0,0,0,0)
R := (R * 10) + D;
10.3: --> R = (0,0,0,0)
XE5: --> R = (-NAN, -NAN, -NAN, -NAN)
The error was caused in line: ca. 4195:
class operator QuadDouble.Multiply(const A: QuadDouble; const B: Double): QuadDouble;
begin
_qd_mul_qd_d(A, @B, Result); // Line: ca. 4195
end;
"Guilty" is therefore _qd_mul_qd_d(A, @b, Result);
The values of parameters A and B are identical with both Delphi versions.
Can it be that a
QuadDouble.X: array [0..3] of Double;
is aligned differently in memory with Delphi XE5 than with Delphi 10.3?
Would any compiler switches possibly be needed for both versions to get the same behaviour?
Unfortunately, I don't have any C knowledge and can't debug the C-source code.
I'm afraid Iโm quite helpless.
Thanks,
Andreas
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.