mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 21:51:40 +03:00

* Fix Issue 20197 - Make std.math.isIdentical work in CTFE * Added tests for function isIdentical Signed-off-by: runiixx <murgua03@gmail.com> * Fix Issue 10386 Signed-off-by: runiixx <murgua03@gmail.com> * fixing style Signed-off-by: runiixx <murgua03@gmail.com> --------- Signed-off-by: runiixx <murgua03@gmail.com> Co-authored-by: Nathan Sashihara <21227491+n8sh@users.noreply.github.com>
1062 lines
29 KiB
D
1062 lines
29 KiB
D
// Written in the D programming language.
|
||
|
||
/**
|
||
This is a submodule of $(MREF std, math).
|
||
|
||
It contains several functions for introspection on numerical values.
|
||
|
||
Copyright: Copyright The D Language Foundation 2000 - 2011.
|
||
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
||
Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
|
||
Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
|
||
Source: $(PHOBOSSRC std/math/traits.d)
|
||
|
||
Macros:
|
||
NAN = $(RED NAN)
|
||
PLUSMN = ±
|
||
INFIN = ∞
|
||
*/
|
||
|
||
module std.math.traits;
|
||
|
||
import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned;
|
||
|
||
|
||
/*********************************
|
||
* Determines if $(D_PARAM x) is NaN.
|
||
* Params:
|
||
* x = a floating point number.
|
||
* Returns:
|
||
* `true` if $(D_PARAM x) is Nan.
|
||
*/
|
||
bool isNaN(X)(X x) @nogc @trusted pure nothrow
|
||
if (isFloatingPoint!(X))
|
||
{
|
||
version (all)
|
||
{
|
||
return x != x;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
Code kept for historical context. At least on Intel, the simple test
|
||
x != x uses one dedicated instruction (ucomiss/ucomisd) that runs in one
|
||
cycle. Code for 80- and 128-bits is larger but still smaller than the
|
||
integrals-based solutions below. Future revisions may enable the code
|
||
below conditionally depending on hardware.
|
||
*/
|
||
alias F = floatTraits!(X);
|
||
static if (F.realFormat == RealFormat.ieeeSingle)
|
||
{
|
||
const uint p = *cast(uint *)&x;
|
||
// Sign bit (MSB) is irrelevant so mask it out.
|
||
// Next 8 bits should be all set.
|
||
// At least one bit among the least significant 23 bits should be set.
|
||
return (p & 0x7FFF_FFFF) > 0x7F80_0000;
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeDouble)
|
||
{
|
||
const ulong p = *cast(ulong *)&x;
|
||
// Sign bit (MSB) is irrelevant so mask it out.
|
||
// Next 11 bits should be all set.
|
||
// At least one bit among the least significant 52 bits should be set.
|
||
return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000;
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeExtended ||
|
||
F.realFormat == RealFormat.ieeeExtended53)
|
||
{
|
||
const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
|
||
const ulong ps = *cast(ulong *)&x;
|
||
return e == F.EXPMASK &&
|
||
ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeQuadruple)
|
||
{
|
||
const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
|
||
const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB];
|
||
const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB];
|
||
return e == F.EXPMASK &&
|
||
(psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0;
|
||
}
|
||
else
|
||
{
|
||
return x != x;
|
||
}
|
||
}
|
||
}
|
||
|
||
///
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
assert( isNaN(float.init));
|
||
assert( isNaN(-double.init));
|
||
assert( isNaN(real.nan));
|
||
assert( isNaN(-real.nan));
|
||
assert(!isNaN(cast(float) 53.6));
|
||
assert(!isNaN(cast(real)-53.6));
|
||
}
|
||
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
import std.meta : AliasSeq;
|
||
|
||
static foreach (T; AliasSeq!(float, double, real))
|
||
{{
|
||
// CTFE-able tests
|
||
assert(isNaN(T.init));
|
||
assert(isNaN(-T.init));
|
||
assert(isNaN(T.nan));
|
||
assert(isNaN(-T.nan));
|
||
assert(!isNaN(T.infinity));
|
||
assert(!isNaN(-T.infinity));
|
||
assert(!isNaN(cast(T) 53.6));
|
||
assert(!isNaN(cast(T)-53.6));
|
||
|
||
// Runtime tests
|
||
shared T f;
|
||
f = T.init;
|
||
assert(isNaN(f));
|
||
assert(isNaN(-f));
|
||
f = T.nan;
|
||
assert(isNaN(f));
|
||
assert(isNaN(-f));
|
||
f = T.infinity;
|
||
assert(!isNaN(f));
|
||
assert(!isNaN(-f));
|
||
f = cast(T) 53.6;
|
||
assert(!isNaN(f));
|
||
assert(!isNaN(-f));
|
||
}}
|
||
}
|
||
|
||
/*********************************
|
||
* Determines if $(D_PARAM x) is finite.
|
||
* Params:
|
||
* x = a floating point number.
|
||
* Returns:
|
||
* `true` if $(D_PARAM x) is finite.
|
||
*/
|
||
bool isFinite(X)(X x) @trusted pure nothrow @nogc
|
||
{
|
||
import std.math.traits : floatTraits, RealFormat;
|
||
|
||
static if (__traits(isFloating, X))
|
||
if (__ctfe)
|
||
return x == x && x != X.infinity && x != -X.infinity;
|
||
alias F = floatTraits!(X);
|
||
ushort* pe = cast(ushort *)&x;
|
||
return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK;
|
||
}
|
||
|
||
///
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
assert( isFinite(1.23f));
|
||
assert( isFinite(float.max));
|
||
assert( isFinite(float.min_normal));
|
||
assert(!isFinite(float.nan));
|
||
assert(!isFinite(float.infinity));
|
||
}
|
||
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
assert(isFinite(1.23));
|
||
assert(isFinite(double.max));
|
||
assert(isFinite(double.min_normal));
|
||
assert(!isFinite(double.nan));
|
||
assert(!isFinite(double.infinity));
|
||
|
||
assert(isFinite(1.23L));
|
||
assert(isFinite(real.max));
|
||
assert(isFinite(real.min_normal));
|
||
assert(!isFinite(real.nan));
|
||
assert(!isFinite(real.infinity));
|
||
|
||
//CTFE
|
||
static assert(isFinite(1.23));
|
||
static assert(isFinite(double.max));
|
||
static assert(isFinite(double.min_normal));
|
||
static assert(!isFinite(double.nan));
|
||
static assert(!isFinite(double.infinity));
|
||
|
||
static assert(isFinite(1.23L));
|
||
static assert(isFinite(real.max));
|
||
static assert(isFinite(real.min_normal));
|
||
static assert(!isFinite(real.nan));
|
||
static assert(!isFinite(real.infinity));
|
||
}
|
||
|
||
|
||
/*********************************
|
||
* Determines if $(D_PARAM x) is normalized.
|
||
*
|
||
* A normalized number must not be zero, subnormal, infinite nor $(NAN).
|
||
*
|
||
* Params:
|
||
* x = a floating point number.
|
||
* Returns:
|
||
* `true` if $(D_PARAM x) is normalized.
|
||
*/
|
||
|
||
/* Need one for each format because subnormal floats might
|
||
* be converted to normal reals.
|
||
*/
|
||
bool isNormal(X)(X x) @trusted pure nothrow @nogc
|
||
{
|
||
import std.math.traits : floatTraits, RealFormat;
|
||
|
||
static if (__traits(isFloating, X))
|
||
if (__ctfe)
|
||
return (x <= -X.min_normal && x != -X.infinity) || (x >= X.min_normal && x != X.infinity);
|
||
alias F = floatTraits!(X);
|
||
ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
|
||
return (e != F.EXPMASK && e != 0);
|
||
}
|
||
|
||
///
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
float f = 3;
|
||
double d = 500;
|
||
real e = 10e+48;
|
||
|
||
assert(isNormal(f));
|
||
assert(isNormal(d));
|
||
assert(isNormal(e));
|
||
f = d = e = 0;
|
||
assert(!isNormal(f));
|
||
assert(!isNormal(d));
|
||
assert(!isNormal(e));
|
||
assert(!isNormal(real.infinity));
|
||
assert(isNormal(-real.max));
|
||
assert(!isNormal(real.min_normal/4));
|
||
|
||
}
|
||
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
// CTFE
|
||
enum float f = 3;
|
||
enum double d = 500;
|
||
enum real e = 10e+48;
|
||
|
||
static assert(isNormal(f));
|
||
static assert(isNormal(d));
|
||
static assert(isNormal(e));
|
||
|
||
static assert(!isNormal(0.0f));
|
||
static assert(!isNormal(0.0));
|
||
static assert(!isNormal(0.0L));
|
||
static assert(!isNormal(real.infinity));
|
||
static assert(isNormal(-real.max));
|
||
static assert(!isNormal(real.min_normal/4));
|
||
}
|
||
|
||
/*********************************
|
||
* Determines if $(D_PARAM x) is subnormal.
|
||
*
|
||
* Subnormals (also known as "denormal number"), have a 0 exponent
|
||
* and a 0 most significant mantissa bit.
|
||
*
|
||
* Params:
|
||
* x = a floating point number.
|
||
* Returns:
|
||
* `true` if $(D_PARAM x) is a denormal number.
|
||
*/
|
||
bool isSubnormal(X)(X x) @trusted pure nothrow @nogc
|
||
{
|
||
import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
|
||
|
||
static if (__traits(isFloating, X))
|
||
if (__ctfe)
|
||
return -X.min_normal < x && x < X.min_normal;
|
||
/*
|
||
Need one for each format because subnormal floats might
|
||
be converted to normal reals.
|
||
*/
|
||
alias F = floatTraits!(X);
|
||
static if (F.realFormat == RealFormat.ieeeSingle)
|
||
{
|
||
uint *p = cast(uint *)&x;
|
||
return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT;
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeDouble)
|
||
{
|
||
uint *p = cast(uint *)&x;
|
||
return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0
|
||
&& (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT);
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeQuadruple)
|
||
{
|
||
ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
|
||
long* ps = cast(long *)&x;
|
||
return (e == 0 &&
|
||
((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0));
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeExtended ||
|
||
F.realFormat == RealFormat.ieeeExtended53)
|
||
{
|
||
ushort* pe = cast(ushort *)&x;
|
||
long* ps = cast(long *)&x;
|
||
|
||
return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0;
|
||
}
|
||
else
|
||
{
|
||
static assert(false, "Not implemented for this architecture");
|
||
}
|
||
}
|
||
|
||
///
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
import std.meta : AliasSeq;
|
||
|
||
static foreach (T; AliasSeq!(float, double, real))
|
||
{{
|
||
T f;
|
||
for (f = 1.0; !isSubnormal(f); f /= 2)
|
||
assert(f != 0);
|
||
}}
|
||
}
|
||
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
static bool subnormalTest(T)()
|
||
{
|
||
T f;
|
||
for (f = 1.0; !isSubnormal(f); f /= 2)
|
||
if (f == 0)
|
||
return false;
|
||
return true;
|
||
}
|
||
static assert(subnormalTest!float());
|
||
static assert(subnormalTest!double());
|
||
static assert(subnormalTest!real());
|
||
}
|
||
|
||
/*********************************
|
||
* Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN).
|
||
* Params:
|
||
* x = a floating point number.
|
||
* Returns:
|
||
* `true` if $(D_PARAM x) is $(PLUSMN)$(INFIN).
|
||
*/
|
||
bool isInfinity(X)(X x) @nogc @trusted pure nothrow
|
||
if (isFloatingPoint!(X))
|
||
{
|
||
import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
|
||
|
||
alias F = floatTraits!(X);
|
||
static if (F.realFormat == RealFormat.ieeeSingle)
|
||
{
|
||
return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeDouble)
|
||
{
|
||
return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
|
||
== 0x7FF0_0000_0000_0000;
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeExtended ||
|
||
F.realFormat == RealFormat.ieeeExtended53)
|
||
{
|
||
const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]);
|
||
const ulong ps = *cast(ulong *)&x;
|
||
|
||
// On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1.
|
||
return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0;
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeQuadruple)
|
||
{
|
||
const long psLsb = (cast(long *)&x)[MANTISSA_LSB];
|
||
const long psMsb = (cast(long *)&x)[MANTISSA_MSB];
|
||
return (psLsb == 0)
|
||
&& (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000;
|
||
}
|
||
else
|
||
{
|
||
return (x < -X.max) || (X.max < x);
|
||
}
|
||
}
|
||
|
||
///
|
||
@nogc @safe pure nothrow unittest
|
||
{
|
||
assert(!isInfinity(float.init));
|
||
assert(!isInfinity(-float.init));
|
||
assert(!isInfinity(float.nan));
|
||
assert(!isInfinity(-float.nan));
|
||
assert(isInfinity(float.infinity));
|
||
assert(isInfinity(-float.infinity));
|
||
assert(isInfinity(-1.0f / 0.0f));
|
||
}
|
||
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
// CTFE-able tests
|
||
assert(!isInfinity(double.init));
|
||
assert(!isInfinity(-double.init));
|
||
assert(!isInfinity(double.nan));
|
||
assert(!isInfinity(-double.nan));
|
||
assert(isInfinity(double.infinity));
|
||
assert(isInfinity(-double.infinity));
|
||
assert(isInfinity(-1.0 / 0.0));
|
||
|
||
assert(!isInfinity(real.init));
|
||
assert(!isInfinity(-real.init));
|
||
assert(!isInfinity(real.nan));
|
||
assert(!isInfinity(-real.nan));
|
||
assert(isInfinity(real.infinity));
|
||
assert(isInfinity(-real.infinity));
|
||
assert(isInfinity(-1.0L / 0.0L));
|
||
|
||
// Runtime tests
|
||
shared float f;
|
||
f = float.init;
|
||
assert(!isInfinity(f));
|
||
assert(!isInfinity(-f));
|
||
f = float.nan;
|
||
assert(!isInfinity(f));
|
||
assert(!isInfinity(-f));
|
||
f = float.infinity;
|
||
assert(isInfinity(f));
|
||
assert(isInfinity(-f));
|
||
f = (-1.0f / 0.0f);
|
||
assert(isInfinity(f));
|
||
|
||
shared double d;
|
||
d = double.init;
|
||
assert(!isInfinity(d));
|
||
assert(!isInfinity(-d));
|
||
d = double.nan;
|
||
assert(!isInfinity(d));
|
||
assert(!isInfinity(-d));
|
||
d = double.infinity;
|
||
assert(isInfinity(d));
|
||
assert(isInfinity(-d));
|
||
d = (-1.0 / 0.0);
|
||
assert(isInfinity(d));
|
||
|
||
shared real e;
|
||
e = real.init;
|
||
assert(!isInfinity(e));
|
||
assert(!isInfinity(-e));
|
||
e = real.nan;
|
||
assert(!isInfinity(e));
|
||
assert(!isInfinity(-e));
|
||
e = real.infinity;
|
||
assert(isInfinity(e));
|
||
assert(isInfinity(-e));
|
||
e = (-1.0L / 0.0L);
|
||
assert(isInfinity(e));
|
||
}
|
||
|
||
@nogc @safe pure nothrow unittest
|
||
{
|
||
import std.meta : AliasSeq;
|
||
static bool foo(T)(inout T x) { return isInfinity(x); }
|
||
foreach (T; AliasSeq!(float, double, real))
|
||
{
|
||
assert(!foo(T(3.14f)));
|
||
assert(foo(T.infinity));
|
||
}
|
||
}
|
||
|
||
/*********************************
|
||
* Is the binary representation of x identical to y?
|
||
*/
|
||
bool isIdentical(real x, real y) @trusted pure nothrow @nogc
|
||
{
|
||
if (__ctfe)
|
||
{
|
||
if (x !is y) return false;
|
||
if (x == x) return true; // If not NaN `is` implies identical representation.
|
||
static if (double.mant_dig != real.mant_dig)
|
||
{
|
||
// Works because we are in CTFE and there is no way in CTFE to set more
|
||
// bits of NaN payload than can fit in a double, and since 2.087
|
||
// changed real.init to be non-signaling I *think* there is no way in
|
||
// CTFE for a real to be a signaling NaN unless real and double have
|
||
// the same representation so real's bits can be manipulated directly.
|
||
double d1 = x, d2 = y;
|
||
}
|
||
else
|
||
{
|
||
// Alias to avoid converting signaling to quiet.
|
||
alias d1 = x;
|
||
alias d2 = y;
|
||
}
|
||
return *cast(long*) &d1 == *cast(long*) &d2;
|
||
}
|
||
// We're doing a bitwise comparison so the endianness is irrelevant.
|
||
long* pxs = cast(long *)&x;
|
||
long* pys = cast(long *)&y;
|
||
alias F = floatTraits!(real);
|
||
static if (F.realFormat == RealFormat.ieeeDouble)
|
||
{
|
||
return pxs[0] == pys[0];
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeQuadruple)
|
||
{
|
||
return pxs[0] == pys[0] && pxs[1] == pys[1];
|
||
}
|
||
else static if (F.realFormat == RealFormat.ieeeExtended)
|
||
{
|
||
ushort* pxe = cast(ushort *)&x;
|
||
ushort* pye = cast(ushort *)&y;
|
||
return pxe[4] == pye[4] && pxs[0] == pys[0];
|
||
}
|
||
else
|
||
{
|
||
assert(0, "isIdentical not implemented");
|
||
}
|
||
}
|
||
///
|
||
@safe @nogc pure nothrow unittest
|
||
{
|
||
// We're forcing the CTFE to run by assigning the result of the function to an enum
|
||
enum test1 = isIdentical(1.0,1.0);
|
||
enum test2 = isIdentical(real.nan,real.nan);
|
||
enum test3 = isIdentical(real.infinity, real.infinity);
|
||
enum test4 = isIdentical(real.infinity, real.infinity);
|
||
enum test5 = isIdentical(0.0, 0.0);
|
||
|
||
assert(test1);
|
||
assert(test2);
|
||
assert(test3);
|
||
assert(test4);
|
||
assert(test5);
|
||
|
||
enum test6 = !isIdentical(0.0, -0.0);
|
||
enum test7 = !isIdentical(real.nan, -real.nan);
|
||
enum test8 = !isIdentical(real.infinity, -real.infinity);
|
||
|
||
assert(test6);
|
||
assert(test7);
|
||
assert(test8);
|
||
}
|
||
|
||
@safe @nogc pure nothrow unittest
|
||
{
|
||
assert( !isIdentical(1.2,1.3));
|
||
assert( isIdentical(0.0, 0.0));
|
||
assert( isIdentical(1.0, 1.0));
|
||
assert( isIdentical(real.infinity, real.infinity));
|
||
assert( isIdentical(-real.infinity, -real.infinity));
|
||
assert( isIdentical(real.nan, real.nan));
|
||
|
||
assert(!isIdentical(0.0, -0.0));
|
||
assert(!isIdentical(real.nan, -real.nan));
|
||
assert(!isIdentical(real.infinity, -real.infinity));
|
||
}
|
||
/*********************************
|
||
* Return 1 if sign bit of e is set, 0 if not.
|
||
*/
|
||
int signbit(X)(X x) @nogc @trusted pure nothrow
|
||
{
|
||
import std.math.traits : floatTraits, RealFormat;
|
||
|
||
if (__ctfe)
|
||
{
|
||
double dval = cast(double) x; // Precision can increase or decrease but sign won't change (even NaN).
|
||
return 0 > *cast(long*) &dval;
|
||
}
|
||
|
||
alias F = floatTraits!(X);
|
||
return ((cast(ubyte *)&x)[F.SIGNPOS_BYTE] & 0x80) != 0;
|
||
}
|
||
|
||
///
|
||
@nogc @safe pure nothrow unittest
|
||
{
|
||
assert(!signbit(float.nan));
|
||
assert(signbit(-float.nan));
|
||
assert(!signbit(168.1234f));
|
||
assert(signbit(-168.1234f));
|
||
assert(!signbit(0.0f));
|
||
assert(signbit(-0.0f));
|
||
assert(signbit(-float.max));
|
||
assert(!signbit(float.max));
|
||
|
||
assert(!signbit(double.nan));
|
||
assert(signbit(-double.nan));
|
||
assert(!signbit(168.1234));
|
||
assert(signbit(-168.1234));
|
||
assert(!signbit(0.0));
|
||
assert(signbit(-0.0));
|
||
assert(signbit(-double.max));
|
||
assert(!signbit(double.max));
|
||
|
||
assert(!signbit(real.nan));
|
||
assert(signbit(-real.nan));
|
||
assert(!signbit(168.1234L));
|
||
assert(signbit(-168.1234L));
|
||
assert(!signbit(0.0L));
|
||
assert(signbit(-0.0L));
|
||
assert(signbit(-real.max));
|
||
assert(!signbit(real.max));
|
||
}
|
||
|
||
@nogc @safe pure nothrow unittest
|
||
{
|
||
// CTFE
|
||
static assert(!signbit(float.nan));
|
||
static assert(signbit(-float.nan));
|
||
static assert(!signbit(168.1234f));
|
||
static assert(signbit(-168.1234f));
|
||
static assert(!signbit(0.0f));
|
||
static assert(signbit(-0.0f));
|
||
static assert(signbit(-float.max));
|
||
static assert(!signbit(float.max));
|
||
|
||
static assert(!signbit(double.nan));
|
||
static assert(signbit(-double.nan));
|
||
static assert(!signbit(168.1234));
|
||
static assert(signbit(-168.1234));
|
||
static assert(!signbit(0.0));
|
||
static assert(signbit(-0.0));
|
||
static assert(signbit(-double.max));
|
||
static assert(!signbit(double.max));
|
||
|
||
static assert(!signbit(real.nan));
|
||
static assert(signbit(-real.nan));
|
||
static assert(!signbit(168.1234L));
|
||
static assert(signbit(-168.1234L));
|
||
static assert(!signbit(0.0L));
|
||
static assert(signbit(-0.0L));
|
||
static assert(signbit(-real.max));
|
||
static assert(!signbit(real.max));
|
||
}
|
||
|
||
/**
|
||
Params:
|
||
to = the numeric value to use
|
||
from = the sign value to use
|
||
Returns:
|
||
a value composed of to with from's sign bit.
|
||
*/
|
||
R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc
|
||
if (isFloatingPoint!(R) && isFloatingPoint!(X))
|
||
{
|
||
import std.math.traits : floatTraits, RealFormat;
|
||
|
||
if (__ctfe)
|
||
{
|
||
return signbit(to) == signbit(from) ? to : -to;
|
||
}
|
||
ubyte* pto = cast(ubyte *)&to;
|
||
const ubyte* pfrom = cast(ubyte *)&from;
|
||
|
||
alias T = floatTraits!(R);
|
||
alias F = floatTraits!(X);
|
||
pto[T.SIGNPOS_BYTE] &= 0x7F;
|
||
pto[T.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80;
|
||
return to;
|
||
}
|
||
|
||
/// ditto
|
||
R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc
|
||
if (isIntegral!(X) && isFloatingPoint!(R))
|
||
{
|
||
return copysign(cast(R) to, from);
|
||
}
|
||
|
||
///
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
assert(copysign(1.0, 1.0) == 1.0);
|
||
assert(copysign(1.0, -0.0) == -1.0);
|
||
assert(copysign(1UL, -1.0) == -1.0);
|
||
assert(copysign(-1.0, -1.0) == -1.0);
|
||
|
||
assert(copysign(real.infinity, -1.0) == -real.infinity);
|
||
assert(copysign(real.nan, 1.0) is real.nan);
|
||
assert(copysign(-real.nan, 1.0) is real.nan);
|
||
assert(copysign(real.nan, -1.0) is -real.nan);
|
||
}
|
||
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
import std.meta : AliasSeq;
|
||
|
||
static foreach (X; AliasSeq!(float, double, real, int, long))
|
||
{
|
||
static foreach (Y; AliasSeq!(float, double, real))
|
||
{{
|
||
X x = 21;
|
||
Y y = 23.8;
|
||
Y e = void;
|
||
|
||
e = copysign(x, y);
|
||
assert(e == 21.0);
|
||
|
||
e = copysign(-x, y);
|
||
assert(e == 21.0);
|
||
|
||
e = copysign(x, -y);
|
||
assert(e == -21.0);
|
||
|
||
e = copysign(-x, -y);
|
||
assert(e == -21.0);
|
||
|
||
static if (isFloatingPoint!X)
|
||
{
|
||
e = copysign(X.nan, y);
|
||
assert(isNaN(e) && !signbit(e));
|
||
|
||
e = copysign(X.nan, -y);
|
||
assert(isNaN(e) && signbit(e));
|
||
}
|
||
}}
|
||
}
|
||
// CTFE
|
||
static foreach (X; AliasSeq!(float, double, real, int, long))
|
||
{
|
||
static foreach (Y; AliasSeq!(float, double, real))
|
||
{{
|
||
enum X x = 21;
|
||
enum Y y = 23.8;
|
||
|
||
assert(21.0 == copysign(x, y));
|
||
assert(21.0 == copysign(-x, y));
|
||
assert(-21.0 == copysign(x, -y));
|
||
assert(-21.0 == copysign(-x, -y));
|
||
|
||
static if (isFloatingPoint!X)
|
||
{
|
||
static assert(isNaN(copysign(X.nan, y)) && !signbit(copysign(X.nan, y)));
|
||
assert(isNaN(copysign(X.nan, -y)) && signbit(copysign(X.nan, -y)));
|
||
}
|
||
}}
|
||
}
|
||
}
|
||
|
||
/*********************************
|
||
Returns `-1` if $(D x < 0), `x` if $(D x == 0), `1` if
|
||
$(D x > 0), and $(NAN) if x==$(NAN).
|
||
*/
|
||
F sgn(F)(F x) @safe pure nothrow @nogc
|
||
if (isFloatingPoint!F || isIntegral!F)
|
||
{
|
||
// @@@TODO@@@: make this faster
|
||
return x > 0 ? 1 : x < 0 ? -1 : x;
|
||
}
|
||
|
||
///
|
||
@safe pure nothrow @nogc unittest
|
||
{
|
||
assert(sgn(168.1234) == 1);
|
||
assert(sgn(-168.1234) == -1);
|
||
assert(sgn(0.0) == 0);
|
||
assert(sgn(-0.0) == 0);
|
||
}
|
||
|
||
/**
|
||
Check whether a number is an integer power of two.
|
||
|
||
Note that only positive numbers can be integer powers of two. This
|
||
function always return `false` if `x` is negative or zero.
|
||
|
||
Params:
|
||
x = the number to test
|
||
|
||
Returns:
|
||
`true` if `x` is an integer power of two.
|
||
*/
|
||
bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc
|
||
if (isNumeric!X)
|
||
{
|
||
import std.math.exponential : frexp;
|
||
|
||
static if (isFloatingPoint!X)
|
||
{
|
||
int exp;
|
||
const X sig = frexp(x, exp);
|
||
|
||
return (exp != int.min) && (sig is cast(X) 0.5L);
|
||
}
|
||
else
|
||
{
|
||
static if (isSigned!X)
|
||
{
|
||
auto y = cast(typeof(x + 0))x;
|
||
return y > 0 && !(y & (y - 1));
|
||
}
|
||
else
|
||
{
|
||
auto y = cast(typeof(x + 0u))x;
|
||
return (y & -y) > (y - 1);
|
||
}
|
||
}
|
||
}
|
||
///
|
||
@safe unittest
|
||
{
|
||
import std.math.exponential : pow;
|
||
|
||
assert( isPowerOf2(1.0L));
|
||
assert( isPowerOf2(2.0L));
|
||
assert( isPowerOf2(0.5L));
|
||
assert( isPowerOf2(pow(2.0L, 96)));
|
||
assert( isPowerOf2(pow(2.0L, -77)));
|
||
|
||
assert(!isPowerOf2(-2.0L));
|
||
assert(!isPowerOf2(-0.5L));
|
||
assert(!isPowerOf2(0.0L));
|
||
assert(!isPowerOf2(4.315));
|
||
assert(!isPowerOf2(1.0L / 3.0L));
|
||
|
||
assert(!isPowerOf2(real.nan));
|
||
assert(!isPowerOf2(real.infinity));
|
||
}
|
||
///
|
||
@safe unittest
|
||
{
|
||
assert( isPowerOf2(1));
|
||
assert( isPowerOf2(2));
|
||
assert( isPowerOf2(1uL << 63));
|
||
|
||
assert(!isPowerOf2(-4));
|
||
assert(!isPowerOf2(0));
|
||
assert(!isPowerOf2(1337u));
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
import std.math.exponential : pow;
|
||
import std.meta : AliasSeq;
|
||
|
||
enum smallP2 = pow(2.0L, -62);
|
||
enum bigP2 = pow(2.0L, 50);
|
||
enum smallP7 = pow(7.0L, -35);
|
||
enum bigP7 = pow(7.0L, 30);
|
||
|
||
static foreach (X; AliasSeq!(float, double, real))
|
||
{{
|
||
immutable min_sub = X.min_normal * X.epsilon;
|
||
|
||
foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L,
|
||
2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2])
|
||
{
|
||
assert( isPowerOf2(cast(X) x));
|
||
assert(!isPowerOf2(cast(X)-x));
|
||
}
|
||
|
||
foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity])
|
||
{
|
||
assert(!isPowerOf2(cast(X) x));
|
||
assert(!isPowerOf2(cast(X)-x));
|
||
}
|
||
}}
|
||
|
||
static foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
|
||
{{
|
||
foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1])
|
||
{
|
||
assert( isPowerOf2(cast(X) x));
|
||
static if (isSigned!X)
|
||
assert(!isPowerOf2(cast(X)-x));
|
||
}
|
||
|
||
foreach (x; [0, 3, 5, 13, 77, X.min, X.max])
|
||
assert(!isPowerOf2(cast(X) x));
|
||
}}
|
||
|
||
// CTFE
|
||
static foreach (X; AliasSeq!(float, double, real))
|
||
{{
|
||
enum min_sub = X.min_normal * X.epsilon;
|
||
|
||
static foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L,
|
||
2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2])
|
||
{
|
||
static assert( isPowerOf2(cast(X) x));
|
||
static assert(!isPowerOf2(cast(X)-x));
|
||
}
|
||
|
||
static foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity])
|
||
{
|
||
static assert(!isPowerOf2(cast(X) x));
|
||
static assert(!isPowerOf2(cast(X)-x));
|
||
}
|
||
}}
|
||
|
||
static foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
|
||
{{
|
||
static foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1])
|
||
{
|
||
static assert( isPowerOf2(cast(X) x));
|
||
static if (isSigned!X)
|
||
static assert(!isPowerOf2(cast(X)-x));
|
||
}
|
||
|
||
static foreach (x; [0, 3, 5, 13, 77, X.min, X.max])
|
||
static assert(!isPowerOf2(cast(X) x));
|
||
}}
|
||
}
|
||
|
||
// Underlying format exposed through floatTraits
|
||
enum RealFormat
|
||
{
|
||
ieeeHalf,
|
||
ieeeSingle,
|
||
ieeeDouble,
|
||
ieeeExtended, // x87 80-bit real
|
||
ieeeExtended53, // x87 real rounded to precision of double.
|
||
ibmExtended, // IBM 128-bit extended
|
||
ieeeQuadruple,
|
||
}
|
||
|
||
// Constants used for extracting the components of the representation.
|
||
// They supplement the built-in floating point properties.
|
||
template floatTraits(T)
|
||
{
|
||
import std.traits : Unqual;
|
||
|
||
// EXPMASK is a ushort mask to select the exponent portion (without sign)
|
||
// EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort
|
||
// EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
|
||
// EXPPOS_SHORT is the index of the exponent when represented as a ushort array.
|
||
// SIGNPOS_BYTE is the index of the sign when represented as a ubyte array.
|
||
// RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal
|
||
enum Unqual!T RECIP_EPSILON = (1/T.epsilon);
|
||
static if (T.mant_dig == 24)
|
||
{
|
||
// Single precision float
|
||
enum ushort EXPMASK = 0x7F80;
|
||
enum ushort EXPSHIFT = 7;
|
||
enum ushort EXPBIAS = 0x3F00;
|
||
enum uint EXPMASK_INT = 0x7F80_0000;
|
||
enum uint MANTISSAMASK_INT = 0x007F_FFFF;
|
||
enum realFormat = RealFormat.ieeeSingle;
|
||
version (LittleEndian)
|
||
{
|
||
enum EXPPOS_SHORT = 1;
|
||
enum SIGNPOS_BYTE = 3;
|
||
}
|
||
else
|
||
{
|
||
enum EXPPOS_SHORT = 0;
|
||
enum SIGNPOS_BYTE = 0;
|
||
}
|
||
}
|
||
else static if (T.mant_dig == 53)
|
||
{
|
||
static if (T.sizeof == 8)
|
||
{
|
||
// Double precision float, or real == double
|
||
enum ushort EXPMASK = 0x7FF0;
|
||
enum ushort EXPSHIFT = 4;
|
||
enum ushort EXPBIAS = 0x3FE0;
|
||
enum uint EXPMASK_INT = 0x7FF0_0000;
|
||
enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only
|
||
enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF;
|
||
enum realFormat = RealFormat.ieeeDouble;
|
||
version (LittleEndian)
|
||
{
|
||
enum EXPPOS_SHORT = 3;
|
||
enum SIGNPOS_BYTE = 7;
|
||
}
|
||
else
|
||
{
|
||
enum EXPPOS_SHORT = 0;
|
||
enum SIGNPOS_BYTE = 0;
|
||
}
|
||
}
|
||
else static if (T.sizeof == 12)
|
||
{
|
||
// Intel extended real80 rounded to double
|
||
enum ushort EXPMASK = 0x7FFF;
|
||
enum ushort EXPSHIFT = 0;
|
||
enum ushort EXPBIAS = 0x3FFE;
|
||
enum realFormat = RealFormat.ieeeExtended53;
|
||
version (LittleEndian)
|
||
{
|
||
enum EXPPOS_SHORT = 4;
|
||
enum SIGNPOS_BYTE = 9;
|
||
}
|
||
else
|
||
{
|
||
enum EXPPOS_SHORT = 0;
|
||
enum SIGNPOS_BYTE = 0;
|
||
}
|
||
}
|
||
else
|
||
static assert(false, "No traits support for " ~ T.stringof);
|
||
}
|
||
else static if (T.mant_dig == 64)
|
||
{
|
||
// Intel extended real80
|
||
enum ushort EXPMASK = 0x7FFF;
|
||
enum ushort EXPSHIFT = 0;
|
||
enum ushort EXPBIAS = 0x3FFE;
|
||
enum realFormat = RealFormat.ieeeExtended;
|
||
version (LittleEndian)
|
||
{
|
||
enum EXPPOS_SHORT = 4;
|
||
enum SIGNPOS_BYTE = 9;
|
||
}
|
||
else
|
||
{
|
||
enum EXPPOS_SHORT = 0;
|
||
enum SIGNPOS_BYTE = 0;
|
||
}
|
||
}
|
||
else static if (T.mant_dig == 113)
|
||
{
|
||
// Quadruple precision float
|
||
enum ushort EXPMASK = 0x7FFF;
|
||
enum ushort EXPSHIFT = 0;
|
||
enum ushort EXPBIAS = 0x3FFE;
|
||
enum realFormat = RealFormat.ieeeQuadruple;
|
||
version (LittleEndian)
|
||
{
|
||
enum EXPPOS_SHORT = 7;
|
||
enum SIGNPOS_BYTE = 15;
|
||
}
|
||
else
|
||
{
|
||
enum EXPPOS_SHORT = 0;
|
||
enum SIGNPOS_BYTE = 0;
|
||
}
|
||
}
|
||
else static if (T.mant_dig == 106)
|
||
{
|
||
// IBM Extended doubledouble
|
||
enum ushort EXPMASK = 0x7FF0;
|
||
enum ushort EXPSHIFT = 4;
|
||
enum realFormat = RealFormat.ibmExtended;
|
||
|
||
// For IBM doubledouble the larger magnitude double comes first.
|
||
// It's really a double[2] and arrays don't index differently
|
||
// between little and big-endian targets.
|
||
enum DOUBLEPAIR_MSB = 0;
|
||
enum DOUBLEPAIR_LSB = 1;
|
||
|
||
// The exponent/sign byte is for most significant part.
|
||
version (LittleEndian)
|
||
{
|
||
enum EXPPOS_SHORT = 3;
|
||
enum SIGNPOS_BYTE = 7;
|
||
}
|
||
else
|
||
{
|
||
enum EXPPOS_SHORT = 0;
|
||
enum SIGNPOS_BYTE = 0;
|
||
}
|
||
}
|
||
else
|
||
static assert(false, "No traits support for " ~ T.stringof);
|
||
}
|
||
|
||
// These apply to all floating-point types
|
||
version (LittleEndian)
|
||
{
|
||
enum MANTISSA_LSB = 0;
|
||
enum MANTISSA_MSB = 1;
|
||
}
|
||
else
|
||
{
|
||
enum MANTISSA_LSB = 1;
|
||
enum MANTISSA_MSB = 0;
|
||
}
|