mirror of
https://github.com/dlang/phobos.git
synced 2025-04-30 23:20:29 +03:00
fix real precision use for inverse and hyperbolic sinh, tanh, scalbn, asinh, acosh (#7651)
This commit is contained in:
parent
cf9b0c7b0d
commit
f85ca8dbe8
1 changed files with 91 additions and 67 deletions
158
std/math.d
158
std/math.d
|
@ -1322,39 +1322,45 @@ float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
|
||||||
* $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no))
|
* $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no))
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
real sinh(real x) @safe pure nothrow @nogc
|
real sinh(real x) @safe pure nothrow @nogc { return _sinh(x); }
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
double sinh(double x) @safe pure nothrow @nogc { return _sinh(x); }
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
float sinh(float x) @safe pure nothrow @nogc { return _sinh(x); }
|
||||||
|
|
||||||
|
///
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
enum sinh1 = (E - 1.0 / E) / 2;
|
||||||
|
import std.meta : AliasSeq;
|
||||||
|
static foreach (F; AliasSeq!(float, double, real))
|
||||||
|
{
|
||||||
|
assert(isIdentical(sinh(F(0.0)), F(0.0)));
|
||||||
|
assert(sinh(F(1.0)).approxEqual(F(sinh1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private F _sinh(F)(F x)
|
||||||
{
|
{
|
||||||
// sinh(x) = (exp(x)-exp(-x))/2;
|
// sinh(x) = (exp(x)-exp(-x))/2;
|
||||||
// Very large arguments could cause an overflow, but
|
// Very large arguments could cause an overflow, but
|
||||||
// the maximum value of x for which exp(x) + exp(-x)) != exp(x)
|
// the maximum value of x for which exp(x) + exp(-x)) != exp(x)
|
||||||
// is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80.
|
// is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80.
|
||||||
if (fabs(x) > real.mant_dig * LN2)
|
if (fabs(x) > F.mant_dig * F(LN2))
|
||||||
{
|
{
|
||||||
return copysign(0.5 * exp(fabs(x)), x);
|
return copysign(F(0.5) * exp(fabs(x)), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
const real y = expm1(x);
|
const y = expm1(x);
|
||||||
return 0.5 * y / (y+1) * (y+2);
|
return F(0.5) * y / (y+1) * (y+2);
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
double sinh(double x) @safe pure nothrow @nogc { return sinh(cast(real) x); }
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real) x); }
|
|
||||||
|
|
||||||
///
|
|
||||||
@safe unittest
|
|
||||||
{
|
|
||||||
assert(isIdentical(sinh(0.0), 0.0));
|
|
||||||
assert(sinh(1.0).approxEqual((E - 1.0 / E) / 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@safe @nogc nothrow unittest
|
@safe @nogc nothrow unittest
|
||||||
{
|
{
|
||||||
assert(equalsDigit(sinh(1.0), (E - 1.0 / E) / 2, useDigits));
|
assert(equalsDigit(sinh(1.0L), real((E - 1.0 / E) / 2), useDigits));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
* Calculates the hyperbolic tangent of x.
|
* Calculates the hyperbolic tangent of x.
|
||||||
*
|
*
|
||||||
|
@ -1364,23 +1370,13 @@ float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real) x); }
|
||||||
* $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no))
|
* $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no))
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
real tanh(real x) @safe pure nothrow @nogc
|
real tanh(real x) @safe pure nothrow @nogc { return _tanh(x); }
|
||||||
{
|
|
||||||
// tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x))
|
|
||||||
if (fabs(x) > real.mant_dig * LN2)
|
|
||||||
{
|
|
||||||
return copysign(1, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
const real y = expm1(2*x);
|
|
||||||
return y / (y + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
double tanh(double x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
|
double tanh(double x) @safe pure nothrow @nogc { return _tanh(x); }
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
|
float tanh(float x) @safe pure nothrow @nogc { return _tanh(x); }
|
||||||
|
|
||||||
///
|
///
|
||||||
@safe unittest
|
@safe unittest
|
||||||
|
@ -1389,9 +1385,21 @@ float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
|
||||||
assert(tanh(1.0).approxEqual(sinh(1.0) / cosh(1.0)));
|
assert(tanh(1.0).approxEqual(sinh(1.0) / cosh(1.0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private F _tanh(F)(F x)
|
||||||
|
{
|
||||||
|
// tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x))
|
||||||
|
if (fabs(x) > F.mant_dig * F(LN2))
|
||||||
|
{
|
||||||
|
return copysign(1, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
const y = expm1(2*x);
|
||||||
|
return y / (y + 2);
|
||||||
|
}
|
||||||
|
|
||||||
@safe @nogc nothrow unittest
|
@safe @nogc nothrow unittest
|
||||||
{
|
{
|
||||||
assert(equalsDigit(tanh(1.0), sinh(1.0) / cosh(1.0), 15));
|
assert(equalsDigit(tanh(1.0L), sinh(1.0L) / cosh(1.0L), 15));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
|
@ -1412,19 +1420,13 @@ float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
|
||||||
* $(SV +$(INFIN),+$(INFIN))
|
* $(SV +$(INFIN),+$(INFIN))
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
real acosh(real x) @safe pure nothrow @nogc
|
real acosh(real x) @safe pure nothrow @nogc { return _acosh(x); }
|
||||||
{
|
|
||||||
if (x > 1/real.epsilon)
|
|
||||||
return LN2 + log(x);
|
|
||||||
else
|
|
||||||
return log(x + sqrt(x*x - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
double acosh(double x) @safe pure nothrow @nogc { return acosh(cast(real) x); }
|
double acosh(double x) @safe pure nothrow @nogc { return _acosh(x); }
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real) x); }
|
float acosh(float x) @safe pure nothrow @nogc { return _acosh(x); }
|
||||||
|
|
||||||
///
|
///
|
||||||
@safe @nogc nothrow unittest
|
@safe @nogc nothrow unittest
|
||||||
|
@ -1436,9 +1438,17 @@ float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real) x); }
|
||||||
assert(isNaN(acosh(0.5)));
|
assert(isNaN(acosh(0.5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private F _acosh(F)(F x) @safe pure nothrow @nogc
|
||||||
|
{
|
||||||
|
if (x > 1/F.epsilon)
|
||||||
|
return F(LN2) + log(x);
|
||||||
|
else
|
||||||
|
return log(x + sqrt(x*x - 1));
|
||||||
|
}
|
||||||
|
|
||||||
@safe @nogc nothrow unittest
|
@safe @nogc nothrow unittest
|
||||||
{
|
{
|
||||||
assert(equalsDigit(acosh(cosh(3.0)), 3, useDigits));
|
assert(equalsDigit(acosh(cosh(3.0L)), 3.0L, useDigits));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
|
@ -1457,20 +1467,13 @@ float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real) x); }
|
||||||
* $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN))
|
* $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN))
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
real asinh(real x) @safe pure nothrow @nogc
|
real asinh(real x) @safe pure nothrow @nogc { return _asinh(x); }
|
||||||
{
|
|
||||||
return (fabs(x) > 1 / real.epsilon)
|
|
||||||
// beyond this point, x*x + 1 == x*x
|
|
||||||
? copysign(LN2 + log(fabs(x)), x)
|
|
||||||
// sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) )
|
|
||||||
: copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
double asinh(double x) @safe pure nothrow @nogc { return asinh(cast(real) x); }
|
double asinh(double x) @safe pure nothrow @nogc { return _asinh(x); }
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real) x); }
|
float asinh(float x) @safe pure nothrow @nogc { return _asinh(x); }
|
||||||
|
|
||||||
///
|
///
|
||||||
@safe @nogc nothrow unittest
|
@safe @nogc nothrow unittest
|
||||||
|
@ -1482,9 +1485,18 @@ float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real) x); }
|
||||||
assert(isNaN(asinh(real.nan)));
|
assert(isNaN(asinh(real.nan)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private F _asinh(F)(F x)
|
||||||
|
{
|
||||||
|
return (fabs(x) > 1 / F.epsilon)
|
||||||
|
// beyond this point, x*x + 1 == x*x
|
||||||
|
? copysign(F(LN2) + log(fabs(x)), x)
|
||||||
|
// sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) )
|
||||||
|
: copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x);
|
||||||
|
}
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
{
|
{
|
||||||
assert(equalsDigit(asinh(sinh(3.0)), 3, useDigits));
|
assert(equalsDigit(asinh(sinh(3.0L)), 3.0L, useDigits));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
|
@ -4010,35 +4022,47 @@ real modf(real x, ref real i) @trusted nothrow @nogc
|
||||||
* $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
|
* $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
real scalbn(real x, int n) @safe pure nothrow @nogc
|
real scalbn(real x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); }
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
double scalbn(double x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); }
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
float scalbn(float x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); }
|
||||||
|
|
||||||
|
///
|
||||||
|
@safe pure nothrow @nogc unittest
|
||||||
{
|
{
|
||||||
|
assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L);
|
||||||
|
assert(scalbn(-real.infinity, 5) == -real.infinity);
|
||||||
|
assert(scalbn(2.0,10) == 2048.0);
|
||||||
|
assert(scalbn(2048.0f,-10) == 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private F _scalbn(F)(F x, int n)
|
||||||
|
{
|
||||||
|
pragma(inline, true);
|
||||||
|
|
||||||
if (__ctfe)
|
if (__ctfe)
|
||||||
{
|
{
|
||||||
// Handle special cases.
|
// Handle special cases.
|
||||||
if (x == 0.0 || isInfinity(x))
|
if (x == F(0.0) || isInfinity(x))
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
return core.math.ldexp(x, n);
|
return core.math.ldexp(x, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
@safe pure nothrow @nogc unittest
|
|
||||||
{
|
|
||||||
assert(scalbn(0x1.2345678abcdefp0, 999) == 0x1.2345678abcdefp999);
|
|
||||||
assert(scalbn(-real.infinity, 5) == -real.infinity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@safe pure nothrow @nogc unittest
|
@safe pure nothrow @nogc unittest
|
||||||
{
|
{
|
||||||
// CTFE-able test
|
// CTFE-able test
|
||||||
static assert(scalbn(0x1.2345678abcdefp0, 999) == 0x1.2345678abcdefp999);
|
static assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L);
|
||||||
static assert(scalbn(-real.infinity, 5) == -real.infinity);
|
static assert(scalbn(-real.infinity, 5) == -real.infinity);
|
||||||
// Test with large exponent delta n where the result is in bounds but 2.0L ^^ n is not.
|
// Test with large exponent delta n where the result is in bounds but 2.0L ^^ n is not.
|
||||||
enum initialExponent = real.min_exp + 2, resultExponent = real.max_exp - 2;
|
enum initialExponent = real.min_exp + 2, resultExponent = real.max_exp - 2;
|
||||||
enum int n = resultExponent - initialExponent;
|
enum int n = resultExponent - initialExponent;
|
||||||
enum real x = 0x1.2345678abcdefp0 * (2.0L ^^ initialExponent);
|
enum real x = 0x1.2345678abcdefp0L * (2.0L ^^ initialExponent);
|
||||||
enum staticResult = scalbn(x, n);
|
enum staticResult = scalbn(x, n);
|
||||||
static assert(staticResult == 0x1.2345678abcdefp0 * (2.0L ^^ resultExponent));
|
static assert(staticResult == 0x1.2345678abcdefp0L * (2.0L ^^ resultExponent));
|
||||||
assert(scalbn(x, n) == staticResult);
|
assert(scalbn(x, n) == staticResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue