* Replaced fpc_ln_real with modern port from fdlibm/uclibc, it has even better accuracy than damath library.

git-svn-id: trunk@27172 -
This commit is contained in:
sergei 2014-03-17 14:54:27 +00:00
parent 9801288cdc
commit 5f985602fb

View File

@ -60,16 +60,9 @@ type
const
PIO2 = 1.57079632679489661923; { pi/2 }
PIO4 = 7.85398163397448309616E-1; { pi/4 }
SQRT2 = 1.41421356237309504880; { sqrt(2) }
SQRTH = 7.07106781186547524401E-1; { sqrt(2)/2 }
LOG2E = 1.4426950408889634073599; { 1/log(2) }
SQ2OPI = 7.9788456080286535587989E-1; { sqrt( 2/pi )}
LOGE2 = 6.93147180559945309417E-1; { log(2) }
LOGSQ2 = 3.46573590279972654709E-1; { log(2)/2 }
THPIO4 = 2.35619449019234492885; { 3*pi/4 }
TWOOPI = 6.36619772367581343075535E-1; { 2/pi }
lossth = 1.073741824e9;
MAXLOG = 8.8029691931113054295988E1; { log(2**127) }
MINLOG = -8.872283911167299960540E1; { log(2**-128) }
@ -1341,124 +1334,162 @@ type
{$ifndef FPC_SYSTEM_HAS_LN}
function fpc_ln_real(d:ValReal):ValReal;compilerproc;
{
This code was translated from uclib code, the original code
had the following copyright notice:
*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*}
{*****************************************************************}
{ Natural Logarithm }
{*****************************************************************}
{ }
{ SYNOPSIS: }
{ }
{ double x, y, log(); }
{ }
{ y = ln( x ); }
{ }
{ DESCRIPTION: }
{ }
{ Returns the base e (2.718...) logarithm of x. }
{ }
{ The argument is separated into its exponent and fractional }
{ parts. If the exponent is between -1 and +1, the logarithm }
{ of the fraction is approximated by }
{ }
{ log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x). }
{ }
{ Otherwise, setting z = 2(x-1)/x+1), }
{ }
{ log(x) = z + z**3 P(z)/Q(z). }
{ }
{*****************************************************************}
const P : array[0..6] of Real = (
{ Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
1/sqrt(2) <= x < sqrt(2) }
{*
* SYNOPSIS:
*
* double x, y, log();
*
* y = ln( x );
*
* DESCRIPTION:
*
* Returns the base e (2.718...) logarithm of x.
*
* Method :
* 1. Argument Reduction: find k and f such that
* x = 2^k * (1+f),
* where sqrt(2)/2 < 1+f < sqrt(2) .
*
* 2. Approximation of log(1+f).
* Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
* = 2s + 2/3 s**3 + 2/5 s**5 + .....,
* = 2s + s*R
* We use a special Reme algorithm on [0,0.1716] to generate
* a polynomial of degree 14 to approximate R The maximum error
* of this polynomial approximation is bounded by 2**-58.45. In
* other words,
* 2 4 6 8 10 12 14
* R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s
* (the values of Lg1 to Lg7 are listed in the program)
* and
* | 2 14 | -58.45
* | Lg1*s +...+Lg7*s - R(z) | <= 2
* | |
* Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
* In order to guarantee error in log below 1ulp, we compute log
* by
* log(1+f) = f - s*(f - R) (if f is not too large)
* log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
*
* 3. Finally, log(x) = k*ln2 + log(1+f).
* = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
* Here ln2 is split into two floating point number:
* ln2_hi + ln2_lo,
* where n*ln2_hi is always exact for |n| < 2000.
*
* Special cases:
* log(x) is NaN with signal if x < 0 (including -INF) ;
* log(+INF) is +INF; log(0) is -INF with signal;
* log(NaN) is that NaN with no signal.
*
* Accuracy:
* according to an error analysis, the error is always less than
* 1 ulp (unit in the last place).
*}
const
ln2_hi: double = 6.93147180369123816490e-01; { 3fe62e42 fee00000 }
ln2_lo: double = 1.90821492927058770002e-10; { 3dea39ef 35793c76 }
two54: double = 1.80143985094819840000e+16; { 43500000 00000000 }
Lg1: double = 6.666666666666735130e-01; { 3FE55555 55555593 }
Lg2: double = 3.999999999940941908e-01; { 3FD99999 9997FA04 }
Lg3: double = 2.857142874366239149e-01; { 3FD24924 94229359 }
Lg4: double = 2.222219843214978396e-01; { 3FCC71C5 1D8E78AF }
Lg5: double = 1.818357216161805012e-01; { 3FC74664 96CB03DE }
Lg6: double = 1.531383769920937332e-01; { 3FC39A09 D078C69F }
Lg7: double = 1.479819860511658591e-01; { 3FC2F112 DF3E5244 }
4.58482948458143443514E-5,
4.98531067254050724270E-1,
6.56312093769992875930E0,
2.97877425097986925891E1,
6.06127134467767258030E1,
5.67349287391754285487E1,
1.98892446572874072159E1);
Q : array[0..5] of Real = (
1.50314182634250003249E1,
8.27410449222435217021E1,
2.20664384982121929218E2,
3.07254189979530058263E2,
2.14955586696422947765E2,
5.96677339718622216300E1);
{ Coefficients for log(x) = z + z**3 P(z)/Q(z),
where z = 2(x-1)/(x+1)
1/sqrt(2) <= x < sqrt(2) }
R : array[0..2] of Real = (
-7.89580278884799154124E-1,
1.63866645699558079767E1,
-6.41409952958715622951E1);
S : array[0..2] of Real = (
-3.56722798256324312549E1,
3.12093766372244180303E2,
-7.69691943550460008604E2);
var e : Integer;
z, y : Real;
zero: double = 0.0;
var
hfsq,f,s,z,R,w,t1,t2,dk: double;
k,hx,i,j: longint;
lx: longword;
begin
if( d <= 0.0 ) then
begin
float_raise(float_flag_invalid);
exit;
end;
d := frexp( d, e );
hx := float64high(d);
lx := float64low(d);
{ logarithm using log(x) = z + z**3 P(z)/Q(z),
where z = 2(x-1)/x+1) }
if( (e > 2) or (e < -2) ) then
begin
if( d < SQRTH ) then
begin
{ 2( 2x-1 )/( 2x+1 ) }
Dec(e, 1);
z := d - 0.5;
y := 0.5 * z + 0.5;
end
else
begin
{ 2 (x-1)/(x+1) }
z := d - 0.5;
z := z - 0.5;
y := 0.5 * d + 0.5;
end;
d := z / y;
{ /* rational form */ }
z := d*d;
z := d + d * ( z * polevl( z, R, 2 ) / p1evl( z, S, 3 ) );
end
else
begin
{ logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) }
if( d < SQRTH ) then
begin
Dec(e, 1);
d := ldexp( d, 1 ) - 1.0; { 2x - 1 }
end
else
d := d - 1.0;
{ rational form }
z := d*d;
y := d * ( z * polevl( d, P, 6 ) / p1evl( d, Q, 6 ) );
y := y - ldexp( z, -1 ); { y - 0.5 * z }
z := d + y;
end;
{ recombine with exponent term }
if( e <> 0 ) then
begin
y := e;
z := z - y * 2.121944400546905827679e-4;
z := z + y * 0.693359375;
end;
result:= z;
k := 0;
if (hx < $00100000) then { x < 2**-1022 }
begin
if (((hx and $7fffffff) or lx)=0) then
exit(-two54/zero); { log(+-0)=-inf }
if (hx<0) then
exit((d-d)/zero); { log(-#) = NaN }
dec(k, 54); d := d * two54; { subnormal number, scale up x }
hx := float64high(d);
end;
if (hx >= $7ff00000) then
exit(d+d);
inc(k, (hx shr 20)-1023);
hx := hx and $000fffff;
i := (hx + $95f64) and $100000;
float64sethigh(d,hx or (i xor $3ff00000)); { normalize x or x/2 }
inc(k, (i shr 20));
f := d-1.0;
if (($000fffff and (2+hx))<3) then { |f| < 2**-20 }
begin
if (f=zero) then
begin
if (k=0) then
exit(zero)
else
begin
dk := k;
exit(dk*ln2_hi+dk*ln2_lo);
end;
end;
R := f*f*(0.5-0.33333333333333333*f);
if (k=0) then
exit(f-R)
else
begin
dk := k;
exit(dk*ln2_hi-((R-dk*ln2_lo)-f));
end;
end;
s := f/(2.0+f);
dk := k;
z := s*s;
i := hx-$6147a;
w := z*z;
j := $6b851-hx;
t1 := w*(Lg2+w*(Lg4+w*Lg6));
t2 := z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
i := i or j;
R := t2+t1;
if (i>0) then
begin
hfsq := 0.5*f*f;
if (k=0) then
result := f-(hfsq-s*(hfsq+R))
else
result := dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f);
end
else
begin
if (k=0) then
result := f-s*(f-R)
else
result := dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f);
end;
end;
{$endif}