summaryrefslogtreecommitdiff
path: root/libphobos/src/std/math/traits.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/math/traits.d')
-rw-r--r--libphobos/src/std/math/traits.d853
1 files changed, 853 insertions, 0 deletions
diff --git a/libphobos/src/std/math/traits.d b/libphobos/src/std/math/traits.d
new file mode 100644
index 00000000000..2841bad219f
--- /dev/null
+++ b/libphobos/src/std/math/traits.d
@@ -0,0 +1,853 @@
+// 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 : 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 : 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 : 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 : 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
+{
+ import std.math : floatTraits, RealFormat;
+
+ // 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
+{
+ 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(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 : 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 : 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));
+ }}
+}
+