[dmd-internals] Pure CTFE-able std.math
Iain Buclaw
ibuclaw at ubuntu.com
Mon Jul 15 09:46:57 PDT 2013
On 15 July 2013 15:04, Daniel Murphy <yebblies at gmail.com> wrote:
> I would prefer to see (as either builtins or properties) a way to get/set
> the exponent/mantissa/sign of a float.
>
> eg
> float x;
> x.sign = 1;
> assert(x.exp == 123);
>
> Easily ctfeable, no need for casting, no endianness problems.
>
> I expect everything could be implemented from these?
>
>
There's also, MANTISSA_LSB and MANTISSA_MSB are used to peek into
reals for 128bit real targets - but it can also apply to 80/96bit real
targets (lsb/msb being of 32bit integers instead of 64bit).
With floor, I've gone down the route of casting between real and
ushort[real.sizeof / short.sizeof] as ushort* alternative was being
optimised away by gcc backend. :o)
I'd be happy to post it in all it's glory. Please note that the other
functions implemented are not endianess specific (apart from the use
of isNaN, isInfinity, frexp).
------------
real ceil(real x) @trusted pure nothrow
{
// Special cases.
if (isNaN(x) || isInfinity(x))
return x;
real y = floor(x);
if (y < x)
y += 1.0L;
return y;
}
real floor(real x) @trusted pure nothrow
{
alias floatTraits!(real) F;
enum REALSZ = real.sizeof / ushort.sizeof;
static if (real.mant_dig == 53)
{
version (LittleEndian)
enum FRACTPOS_SHORT = 0;
else
enum FRACTPOS_SHORT = 3;
}
else static if (real.mant_dig == 64)
{
version (LittleEndian)
enum FRACTPOS_SHORT = 0;
else
enum FRACTPOS_SHORT = 4;
}
else if (real.mant_dig == 113)
{
version (LittleEndian)
enum FRACTPOS_SHORT = 0;
else
enum FRACTPOS_SHORT = 7;
}
else
static assert(false, "Only 64-bit, 80-bit, and 128-bit reals
are supported");
// Bit clearing masks.
static immutable ushort[17] BMASK = [
0xffff, 0xfffe, 0xfffc, 0xfff8,
0xfff0, 0xffe0, 0xffc0, 0xff80,
0xff00, 0xfe00, 0xfc00, 0xf800,
0xf000, 0xe000, 0xc000, 0x8000,
0x0000,
];
// Special cases.
if (isNaN(x) || isInfinity(x) || x == 0.0L)
return x;
// Find the exponent (power of 2)
ushort[REALSZ] vu = *cast(ushort[REALSZ]*)&x;
int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
if (exp < 0)
{
if (x < 0)
return -1.0L;
else
return 0.0L;
}
int j = FRACTPOS_SHORT;
exp = (real.mant_dig - 1) - exp;
// Clean out 16 bits at a time.
while (exp >= 16)
{
version (LittleEndian)
vu[j++] = 0;
else
vu[j--] = 0;
exp -= 16;
}
// Clear the remaining bits.
if (exp > 0)
vu[j] &= BMASK[exp];
real y = *cast(real*)&vu;
if ((x < 0.0L) && (y != x))
y -= 1.0L;
return y;
}
Regards
--
Iain Buclaw
*(p < e ? p++ : p) = (c & 0x0f) + '0';
More information about the dmd-internals
mailing list