Metaprog can be abstruse

Dom DiSc dominikus at scherkl.de
Mon Jan 2 15:15:05 UTC 2023


On Sunday, 1 January 2023 at 23:05:46 UTC, kdevel wrote:
> On Wednesday, 28 December 2022 at 13:57:13 UTC, Dom Disc wrote:
>> [...]
>> I now have a function
>>
>> ```d
>> ubyte maxpow(const ulong base) { }
>> ```
>
> Code?
>
>> That returns the maximum power of the given base that fits in 
>> a ucent
>
> ucent? Cannot use cent/ucent or core.int128's types here.

Yeah, I implemented this long time ago, when I still had hope 
that ucent will be available soon :-/

>
>> (for smaller target types, shift down the result by one for 
>> each halfing of the size).
>
> What do you mean by "shift down"? "Shift right" aka "divide by 
> two"?

Yes. Divide by two to find out what power fits in uint, divide by 
four for ushort and divide by 8 for ubyte.

> [...]
> This generates too few elements.

Ok, but I tend to omit n^^0 and n^^1 - so I would say it even 
generates too many elements :-)

>> ```d
>> static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17);
>> ```
>
> Unnecessary code duplication.

Yes, I hate that too. But

```d
ulong[] ranksOf17 = powersOf(17);
```

will generate a dynamic array, and unfortunately an inherited 
length for static arrays (e.g. something like uint[$] x = 
[1,2,3]; or whatever syntax we could agree upon) is still not 
available :-(

> Unit tests?

Of course. Nothing that you can do will ever make those obsolete.
Mine are rather exhaustive:

```d
/// exponentiation without overflow
/// return invalid if overflow would occur.
T safePow(T)(const(T) base, const int exp) if(isIntegral!T)
{
    if(exp < 2) return !exp ? 1 : (exp < 0 && base.abs != 1) ? 0 : 
odd(exp) ? base : 1;
    static if(isUnsigned!T)
    {
       if(base < 2) return base;
       if(base >= 1<<(T.sizeof<<2)) return invalid!T; // base too 
large to fit any powers of it into the same type
       if(exp > (maxpow(base)>>(5-bitlen(T.sizeof)))) return 
invalid!T;
       return T(base^^exp); // calc only if no overflow for sure 
(very efficient)
    }
    else
    {
       auto r = safePow(abs(base), exp); // max power may not fit 
in the signed type
       if(r > T.max) return invalid!T; // but at least we can 
detect it
       return (base < 0 && odd(exp)) ? -T(r) : T(r);
    }
}

unittest
{
    import std.conv;
    void test(T)()
    {
       T r;
       foreach(T base; (-128 * isSigned!T)..128)
       {
          foreach(uint exp; 0..256)
          {
             r = safePow(base, exp);
             if(exp==1) assert(r == base);
             else if(r == invalid!T)
             {
                assert(T.max / safePow(base, exp-1).abs < base.abs,
                       "max wrong: ",base,"^^",exp," <= 
"~T.stringof~".max");
                break;
             }
             assert(r == base^^exp, "calc wrong: ",base,"^^",exp," 
!= ",r);
          }
       }
    }
    test!byte;
    test!ubyte;
    test!short;
    test!ushort;
    test!int;
    test!uint;
    test!long;
    test!ulong;
}
```



More information about the Digitalmars-d mailing list