mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-26 22:03:48 +02:00
180 lines
4.5 KiB
ObjectPascal
180 lines
4.5 KiB
ObjectPascal
{
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
|
|
* for details about the copyright. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
Authors: Alexander Klenin, Werner Pamler
|
|
|
|
}
|
|
unit TAMath;
|
|
|
|
{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils;
|
|
|
|
function CumulNormDistr(AX: Double): Double;
|
|
function InvCumulNormDistr(AX: Double): Double;
|
|
|
|
procedure EnsureOrder(var A, B: Integer); overload; inline;
|
|
procedure EnsureOrder(var A, B: Double); overload; inline;
|
|
|
|
procedure ExpandRange(var ALo, AHi: Double; ACoeff: Double);
|
|
|
|
function SafeInfinity: Double; inline;
|
|
function SafeInRange(AValue, ABound1, ABound2: Double): Boolean;
|
|
function SafeMin(A, B: Double): Double;
|
|
function SafeNan: Double; inline;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math, spe, TAChartUtils;
|
|
|
|
// Cumulative normal distribution
|
|
// x = -INF ... INF --> Result = 0 ... 1
|
|
function CumulNormDistr(AX: Double): Double;
|
|
begin
|
|
if AX > 0 then
|
|
Result := (speerf(AX / Sqrt(2)) + 1) * 0.5
|
|
else if AX < 0 then
|
|
Result := (1 - speerf(-AX / Sqrt(2))) * 0.5
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
// Inverse cumulative normal distribution.
|
|
// x = 0 ... 1 --> Result = -INF ... +INF
|
|
// Algorithm by Peter John Acklam.
|
|
// http://home.online.no/~pjacklam/notes/invnorm/index.html
|
|
function InvCumulNormDistr(AX: Double): Double;
|
|
const
|
|
A: array[1..6] of Double = (
|
|
-3.969683028665376e+01,
|
|
+2.209460984245205e+02,
|
|
-2.759285104469687e+02,
|
|
+1.383577518672690e+02,
|
|
-3.066479806614716e+01,
|
|
+2.506628277459239e+00
|
|
);
|
|
B: array[1..5] of Double = (
|
|
-5.447609879822406e+01,
|
|
+1.615858368580409e+02,
|
|
-1.556989798598866e+02,
|
|
+6.680131188771972e+01,
|
|
-1.328068155288572e+01
|
|
);
|
|
C: array[1..6] of Double = (
|
|
-7.784894002430293e-03,
|
|
-3.223964580411365e-01,
|
|
-2.400758277161838e+00,
|
|
-2.549732539343734e+00,
|
|
+4.374664141464968e+00,
|
|
+2.938163982698783e+00
|
|
);
|
|
D: array[1..4] of Double = (
|
|
+7.784695709041462e-03,
|
|
+3.224671290700398e-01,
|
|
+2.445134137142996e+00,
|
|
+3.754408661907416e+00
|
|
);
|
|
// Switching points between regions.
|
|
P_LOW = 0.02425;
|
|
P_HIGH = 1 - P_LOW;
|
|
var
|
|
q, r: Extended;
|
|
begin
|
|
if AX <= 0 then
|
|
Result := NegInfinity
|
|
else if AX < P_LOW then begin
|
|
// Rational approximation for lower region.
|
|
q := Sqrt(-2 * Ln(AX));
|
|
Result :=
|
|
(((((C[1] * q + C[2]) * q + C[3]) * q + C[4]) * q + C[5]) * q + C[6]) /
|
|
((((D[1] * q + D[2]) * q + D[3]) * q + D[4]) * q + 1);
|
|
end
|
|
else if AX <= P_HIGH then begin
|
|
// Rational approximation for central region.
|
|
q := AX - 0.5 ;
|
|
r := q * q ;
|
|
Result :=
|
|
(((((A[1] * r + A[2]) * r + A[3]) * r + A[4]) * r + A[5]) * r + A[6]) * q /
|
|
(((((B[1] * r + B[2]) * r + B[3]) * r + B[4]) * r + B[5]) * r + 1);
|
|
end
|
|
else if AX < 1 then begin
|
|
// Rational approximation for upper region.
|
|
q := Sqrt(-2 * Ln(1 - AX));
|
|
Result :=
|
|
-(((((C[1] * q + C[2]) * q + C[3]) * q + C[4]) * q + C[5]) * q + C[6]) /
|
|
((((D[1] * q + D[2]) * q + D[3]) * q + D[4]) * q + 1);
|
|
end else
|
|
Result := SafeInfinity;
|
|
end;
|
|
|
|
procedure EnsureOrder(var A, B: Integer); overload; inline;
|
|
begin
|
|
if A > B then
|
|
Exchange(A, B);
|
|
end;
|
|
|
|
procedure EnsureOrder(var A, B: Double); overload; inline;
|
|
begin
|
|
if A > B then
|
|
Exchange(A, B);
|
|
end;
|
|
|
|
procedure ExpandRange(var ALo, AHi: Double; ACoeff: Double);
|
|
var
|
|
d: Double;
|
|
begin
|
|
if IsInfinite(ALo) or IsInfinite(AHi) then exit;
|
|
d := AHi - ALo;
|
|
ALo -= d * ACoeff;
|
|
AHi += d * ACoeff;
|
|
end;
|
|
|
|
function SafeInfinity: Double;
|
|
begin
|
|
{$PUSH}{$R-}{$Q-}
|
|
Result := Infinity;
|
|
{$POP}
|
|
end;
|
|
|
|
function SafeInRange(AValue, ABound1, ABound2: Double): Boolean;
|
|
begin
|
|
EnsureOrder(ABound1, ABound2);
|
|
Result := InRange(AValue, ABound1, ABound2);
|
|
end;
|
|
|
|
function SafeMin(A, B: Double): Double;
|
|
begin
|
|
if IsNan(A) then
|
|
Result := B
|
|
else if IsNan(B) then
|
|
Result := A
|
|
else if A < B then
|
|
Result := A
|
|
else
|
|
Result := B;
|
|
end;
|
|
|
|
function SafeNan: Double;
|
|
begin
|
|
{$PUSH}{$R-}{$Q-}
|
|
Result := NaN;
|
|
{$POP}
|
|
end;
|
|
|
|
end.
|
|
|