Merge pull request #4327 from tsbockman/isPowerOf2

Add `std.math.isPowerOf2()`. Supports floating-point and integers.
This commit is contained in:
Andrei Alexandrescu 2016-06-16 23:09:02 -04:00 committed by GitHub
commit 7172eda466
8 changed files with 131 additions and 46 deletions

View file

@ -4632,7 +4632,8 @@ if (isForwardRange!R && !isRandomAccessRange!R)
private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
if (isForwardRange!R && !isRandomAccessRange!R)
{
static assert(!(N & (N-1))); //isPow2
import std.math : isPowerOf2;
static assert(isPowerOf2(N));
static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
+ sumPairwiseN!(N/2, needEmptyChecks, F)(r);

View file

@ -22,6 +22,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
import std.conv, std.experimental.allocator.common, std.traits;
import std.algorithm : min;
import std.typecons : Ternary;
import std.math : isPowerOf2;
static assert(
!stateSize!Prefix || Allocator.alignment >= Prefix.alignof,

View file

@ -305,6 +305,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
*/
void[] alignedAllocate(size_t n, uint a)
{
import std.math : isPowerOf2;
assert(a.isPowerOf2);
if (a <= alignment) return allocate(n);

View file

@ -164,6 +164,7 @@ struct Region(ParentAllocator = NullAllocator,
*/
void[] alignedAllocate(size_t n, uint a)
{
import std.math : isPowerOf2;
assert(a.isPowerOf2);
static if (growDownwards)
{

View file

@ -7,6 +7,7 @@ Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`)
*/
module std.experimental.allocator.common;
import std.algorithm, std.traits;
import std.math : isPowerOf2;
/**
Returns the size in bytes of the state that needs to be allocated to hold an
@ -307,33 +308,6 @@ package void* alignUpTo(void* ptr, uint alignment)
return slack ? ptr + alignment - slack : ptr;
}
// Credit: Matthias Bentrup
/**
Returns `true` if `x` is a nonzero power of two.
*/
@safe @nogc nothrow pure
package bool isPowerOf2(uint x)
{
return (x & -x) > (x - 1);
}
@safe @nogc nothrow pure
unittest
{
assert(!isPowerOf2(0));
assert(isPowerOf2(1));
assert(isPowerOf2(2));
assert(!isPowerOf2(3));
assert(isPowerOf2(4));
assert(!isPowerOf2(5));
assert(!isPowerOf2(6));
assert(!isPowerOf2(7));
assert(isPowerOf2(8));
assert(!isPowerOf2(9));
assert(!isPowerOf2(10));
assert(isPowerOf2(1UL << 31));
}
@safe @nogc nothrow pure
package bool isGoodStaticAlignment(uint x)
{

View file

@ -47,7 +47,7 @@ $(TR $(TDNW Floating-point operations) $(TD
$(TR $(TDNW Introspection) $(TD
$(MYREF isFinite) $(MYREF isIdentical) $(MYREF isInfinity) $(MYREF isNaN)
$(MYREF isNormal) $(MYREF isSubnormal) $(MYREF signbit) $(MYREF sgn)
$(MYREF copysign)
$(MYREF copysign) $(MYREF isPowerOf2)
))
$(TR $(TDNW Complex Numbers) $(TD
$(MYREF abs) $(MYREF conj) $(MYREF sin) $(MYREF cos) $(MYREF expi)
@ -7619,3 +7619,116 @@ T truncPow2(T)(const T val) if (isFloatingPoint!T)
assert(truncPow2(T.init).isNaN);
}
}
/**
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)
{
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)
alias W = typeof(x + 0);
else
alias W = typeof(x + 0u); // For ubyte and ushort W should be uint
static if (is(X == W))
{
static if (isSigned!X)
return ((x & -x) == x) && (x > -x);
else
return (x & -x) > (x - 1);
}
else
{
pragma(inline, true);
return isPowerOf2(cast(W)x);
}
}
}
///
unittest
{
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));
}
///
unittest
{
assert( isPowerOf2(1));
assert( isPowerOf2(2));
assert( isPowerOf2(1uL << 63));
assert(!isPowerOf2(-4));
assert(!isPowerOf2(0));
assert(!isPowerOf2(1337u));
}
unittest
{
import std.meta : AliasSeq;
immutable smallP2 = pow(2.0L, -62);
immutable bigP2 = pow(2.0L, 50);
immutable smallP7 = pow(7.0L, -35);
immutable bigP7 = pow(7.0L, 30);
foreach (X; AliasSeq!(float, double, real))
{
immutable min_sub = X.min_normal * X.epsilon;
foreach (x; AliasSeq!(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; AliasSeq!(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));
}
}
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));
}
}

View file

@ -2630,7 +2630,7 @@ private:
in
{
assert(range.length >= 4);
assert(isPowerOfTwo(range.length));
assert(isPowerOf2(range.length));
}
body
{
@ -2664,7 +2664,7 @@ private:
in
{
assert(range.length >= 4);
assert(isPowerOfTwo(range.length));
assert(isPowerOf2(range.length));
}
body
{
@ -2772,7 +2772,7 @@ private:
void butterfly(R)(R buf) const
in
{
assert(isPowerOfTwo(buf.length));
assert(isPowerOf2(buf.length));
}
body
{
@ -2859,7 +2859,7 @@ private:
return;
}
enforce(isPowerOfTwo(size),
enforce(isPowerOf2(size),
"Can only do FFTs on ranges with a size that is a power of two.");
auto table = new lookup_t[][bsf(size) + 1];
@ -3287,13 +3287,6 @@ void slowFourier4(Ret, R)(R range, Ret buf)
buf[3] = range[0] + range[1] * C(0, 1) - range[2] - range[3] * C(0, 1);
}
bool isPowerOfTwo(N)(N num)
if (isScalarType!N && !isFloatingPoint!N)
{
import core.bitop : bsf, bsr;
return bsr(num) == bsf(num);
}
N roundDownToPowerOf2(N)(N num)
if (isScalarType!N && !isFloatingPoint!N)
{

View file

@ -1105,7 +1105,7 @@ template PackedPtr(T)
@trusted struct PackedPtrImpl(T, size_t bits)
{
pure nothrow:
static assert(isPowerOf2(bits));
static assert(isPow2OrZero(bits));
this(inout(size_t)* ptr)inout @safe @nogc
{
@ -1503,7 +1503,7 @@ string genUnrolledSwitchSearch(size_t size)
import core.bitop : bsr;
import std.array : replace;
import std.conv : to;
assert(isPowerOf2(size));
assert(isPow2OrZero(size));
string code = `
import core.bitop : bsr;
auto power = bsr(m)+1;
@ -1533,15 +1533,16 @@ string genUnrolledSwitchSearch(size_t size)
return code;
}
bool isPowerOf2(size_t sz) @safe pure nothrow @nogc
bool isPow2OrZero(size_t sz) @safe pure nothrow @nogc
{
// See also: std.math.isPowerOf2()
return (sz & (sz-1)) == 0;
}
size_t uniformLowerBound(alias pred, Range, T)(Range range, T needle)
if (is(T : ElementType!Range))
{
assert(isPowerOf2(range.length));
assert(isPow2OrZero(range.length));
size_t idx = 0, m = range.length/2;
while (m != 0)
{
@ -1557,7 +1558,7 @@ size_t uniformLowerBound(alias pred, Range, T)(Range range, T needle)
size_t switchUniformLowerBound(alias pred, Range, T)(Range range, T needle)
if (is(T : ElementType!Range))
{
assert(isPowerOf2(range.length));
assert(isPow2OrZero(range.length));
size_t idx = 0, m = range.length/2;
enum max = 1<<10;
while (m >= max)
@ -1580,7 +1581,7 @@ template sharMethod(alias uniLowerBound)
alias pred = binaryFun!_pred;
if (range.length == 0)
return 0;
if (isPowerOf2(range.length))
if (isPow2OrZero(range.length))
return uniLowerBound!pred(range, needle);
size_t n = truncPow2(range.length);
if (pred(range[n-1], needle))