On Friday, 24 June 2016 at 16:56:36 UTC, Andrei Alexandrescu
wrote:
There are a few simple enhancements to add to core.checkedint:
1. No need for different names (addu/adds) etc., overloading
should take care of it (simplifies caller code). Right now the
API is an odd mix of overloading and distinct names.
2. The overflow flag should be an integral, not a bool, so you
get to count how many overflows there were. This is minor but
has no extra cost on the happy case.
3. There should be an exponentiation function.
So I'm thinking of the signatures:
pure nothrow @nogc @safe
{
int add(int x, int y, ref uint overflows);
uint add(uint x, uint y, ref uint overflows);
long add(long x, int y, ref uint overflows);
ulong add(ulong x, uint y, ref uint overflows);
int mul(int x, int y, ref uint overflows);
uint mul(uint x, uint y, ref uint overflows);
long mul(long x, int y, ref uint overflows);
ulong mul(ulong x, uint y, ref uint overflows);
int sub(int x, int y, ref uint overflows);
uint sub(uint x, uint y, ref uint overflows);
long sub(long x, int y, ref uint overflows);
ulong sub(ulong x, uint y, ref uint overflows);
int neg(int x, ref uint overflows);
long neg(long x, ref uint overflows);
int pow(int x, int y, ref uint overflows);
uint pow(uint x, uint y, ref uint overflows);
long pow(long x, int y, ref uint overflows);
ulong pow(ulong x, uint y, ref uint overflows);
}
Thoughts?
Andrei
I have a save exponentiation function (+ two helper functions
that may also be useful on their own):
/// add a property to numeric types that can be used as return
value if a result is out of bounds
template invalid(T) if(isNumeric!T)
{
static if(isFloatingPoint!T)
@property enum invalid = T.init;
else static if(isSigned!T)
@property enum invalid = T.min; // 0x80..00
else // unsigned
@property enum invalid = T.max; // 0xFF..FF
}
/// calculate the maximal power of the value that would fit in an
ucent
/// for smaller types simply shift down the result
/// (e.g. divide by 4 to calc the maxpower that would fit in an
uint)
ubyte maxpow(const ulong a) pure @safe @nogc nothrow
{
assert(a>1); // no useful maxpower exists for 0 and 1
static immutable ubyte[55] mp = [ 127, 80, 63, 55, 49, 45, 43,
40,
38, 37, 35, 34, 33, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27,
27,
27, 26, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24,
23,
23, 23, 23, 23, 23, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22
];
return (a<139) ? (a<57) ? mp[cast(ubyte)a-2]
: ((a<85) ? ((a<69) ? 21 : 20)
: ((a<107) ? 19 : 18))
: ((a<7132) ? ((a<566) ? ((a<256) ? ((a<185) ? 17 : 16)
: ((a<371) ? 15 : 14))
: ((a<1626) ? ((a<921) ? 13 : 12)
: ((a<3184) ? 11 : 10)))
: ((a<2642246) ? ((a<65536) ? ((a<19113) ? 9 : 8)
: ((a<319558) ? 7 : 6))
: ((a<4294967296) ? ((a<50859009) ? 5 : 4)
: ((a<6981463658332) ? 3 : 2))));
}
/// exponentiation without overflow
/// return invalid if overflow would occur.
T safePow(T)(const(T) base, const ubyte exp) pure @safe @nogc
nothrow if(isIntegral!T)
{
static if(isUnsigned!T)
{
if(!exp) return 1; // x^^0 is always 1
if(exp == 1 || base < 2) return base; // x^^1 = x, 1^^n = 1
and 0^^n = 0, so do nothing
static if(T.sizeof > ulong.sizeof)
{
if(base > ulong.max) return invalid!T;
}
if(exp > (maxpow(base)>>(5-bitlen(T.sizeof)))) return
invalid!T;
return base ^^ exp; // calc only if no overflow for sure
(very efficient)
}
else
{
auto r = safePow(abs(base), exp); // sometimes calc even if
result won't fit
if(r > T.max) return invalid!T; // but at least we can
easily detect if it happened
return (base < 0 && odd(exp)) ? -cast(T)r : cast(T)r;
}
}
unittest
{
void test(T)()
{
T r;
foreach(base; T.min..T.max+1)
{
foreach(exp; 0..256)
{
r = safePow(base, exp);
if(r == invalid!T)
{
assert(base^^exp > T.max, "max wrong:
"+T.stringOf());
break;
}
assert(r == base^^exp, "calc wrong");
}
}
}
test!byte;
test!ubyte;
test!short;
test!ushort;
/+ test!int;
test!uint;
test!long;
test!ulong; +/
}