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