core.checkedint

Dominikus Dittes Scherkl via Digitalmars-d digitalmars-d at puremagic.com
Fri Jun 24 10:25:32 PDT 2016


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; +/
}



More information about the Digitalmars-d mailing list