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) private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
if (isForwardRange!R && !isRandomAccessRange!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); static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
else return sumPairwiseN!(N/2, needEmptyChecks, F)(r) else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
+ 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.conv, std.experimental.allocator.common, std.traits;
import std.algorithm : min; import std.algorithm : min;
import std.typecons : Ternary; import std.typecons : Ternary;
import std.math : isPowerOf2;
static assert( static assert(
!stateSize!Prefix || Allocator.alignment >= Prefix.alignof, !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) void[] alignedAllocate(size_t n, uint a)
{ {
import std.math : isPowerOf2;
assert(a.isPowerOf2); assert(a.isPowerOf2);
if (a <= alignment) return allocate(n); if (a <= alignment) return allocate(n);

View file

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

View file

@ -7,6 +7,7 @@ Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`)
*/ */
module std.experimental.allocator.common; module std.experimental.allocator.common;
import std.algorithm, std.traits; 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 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; 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 @safe @nogc nothrow pure
package bool isGoodStaticAlignment(uint x) package bool isGoodStaticAlignment(uint x)
{ {

View file

@ -47,7 +47,7 @@ $(TR $(TDNW Floating-point operations) $(TD
$(TR $(TDNW Introspection) $(TD $(TR $(TDNW Introspection) $(TD
$(MYREF isFinite) $(MYREF isIdentical) $(MYREF isInfinity) $(MYREF isNaN) $(MYREF isFinite) $(MYREF isIdentical) $(MYREF isInfinity) $(MYREF isNaN)
$(MYREF isNormal) $(MYREF isSubnormal) $(MYREF signbit) $(MYREF sgn) $(MYREF isNormal) $(MYREF isSubnormal) $(MYREF signbit) $(MYREF sgn)
$(MYREF copysign) $(MYREF copysign) $(MYREF isPowerOf2)
)) ))
$(TR $(TDNW Complex Numbers) $(TD $(TR $(TDNW Complex Numbers) $(TD
$(MYREF abs) $(MYREF conj) $(MYREF sin) $(MYREF cos) $(MYREF expi) $(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); 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 in
{ {
assert(range.length >= 4); assert(range.length >= 4);
assert(isPowerOfTwo(range.length)); assert(isPowerOf2(range.length));
} }
body body
{ {
@ -2664,7 +2664,7 @@ private:
in in
{ {
assert(range.length >= 4); assert(range.length >= 4);
assert(isPowerOfTwo(range.length)); assert(isPowerOf2(range.length));
} }
body body
{ {
@ -2772,7 +2772,7 @@ private:
void butterfly(R)(R buf) const void butterfly(R)(R buf) const
in in
{ {
assert(isPowerOfTwo(buf.length)); assert(isPowerOf2(buf.length));
} }
body body
{ {
@ -2859,7 +2859,7 @@ private:
return; return;
} }
enforce(isPowerOfTwo(size), enforce(isPowerOf2(size),
"Can only do FFTs on ranges with a size that is a power of two."); "Can only do FFTs on ranges with a size that is a power of two.");
auto table = new lookup_t[][bsf(size) + 1]; 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); 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) N roundDownToPowerOf2(N)(N num)
if (isScalarType!N && !isFloatingPoint!N) if (isScalarType!N && !isFloatingPoint!N)
{ {

View file

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