Improve conversion functions.

This commit is contained in:
Christoffer Lerno
2022-10-20 20:32:33 +02:00
parent ae7aa65f35
commit e15dbd4907
2 changed files with 75 additions and 126 deletions

View File

@@ -54,13 +54,15 @@ const DOUBLE_MIN_10_EXP = -307;
const DOUBLE_MAX_EXP = 1024;
const DOUBLE_MIN_EXP = -1021;
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
const QUAD_MANT_DIG = 113;
/*
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
const QUAD_DIG = 33;
const QUAD_DEC_DIGITS = 36;
const QUAD_MANT_DIG = 113;
const QUAD_MAX_10_EXP = 4932;
const QUAD_MIN_10_EXP = -4931;
const QUAD_MAX_EXP = 16384;

View File

@@ -70,125 +70,62 @@ fn int128 __modti3(int128 a, int128 b) @extname("__modti3") @weak
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
}
private union DoubleBits
fn float __floattisf(int128 a) @extname("__floattisf") @weak = float_from_i128(float, a);
fn double __floattidf(int128 a) @extname("__floattidf") @weak = float_from_i128(double, a);
fn float __floatuntisf(uint128 a) @extname("__floatuntisf") @weak = float_from_u128(float, a);
fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak = float_from_u128(double, a);
fn uint128 __fixunsdfti(double a) @weak @extname("__fixunsdfti") = fixuint(a);
fn uint128 __fixunssfti(float a) @weak @extname("__fixunssfti") = fixuint(a);
fn int128 __fixdfti(double a) @weak @extname("__fixdfti") = fixint(a);
fn int128 __fixsfti(float a) @weak @extname("__fixsfti") = fixint(a);
private macro float_from_i128($Type, a)
{
ulong l;
double d;
}
private union FloatBits
{
uint i;
float f;
}
fn double __floattidf(int128 a) @extname("__floattidf") @weak
{
if (a == 0) return 0.0;
$switch ($Type):
$case double:
var $Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
const SIGNIFICANT_BITS = 52;
const EXP_BIAS = 1023;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
const SIGN_BIT = 1u64 << 63;
$case float:
var $Rep = uint;
const MANT_DIG = FLOAT_MANT_DIG;
const EXP_BIAS = 127;
const SIGNIFICANT_BITS = 23;
const MANTISSA_MASK = 0x7F_FFFFu32;
const SIGN_BIT = 1u32 << 31;
$case float16:
var $Rep = ushort;
const MANT_DIG = HALF_MANT_DIG;
$case float128:
var $Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
$endswitch;
if (a == 0) return ($Type)0;
// Grab and remove sign.
int128 sign = a >> 127;
a = (a ^ sign) - sign;
int sd = 128 - (int)$$clz(a);
int e = sd - 1;
if (sd > DOUBLE_MANT_DIG)
{
switch (sd)
{
case DOUBLE_MANT_DIG + 1:
a <<= 1;
case DOUBLE_MANT_DIG + 2:
break;
default:
a = ((uint128)a >> (sd - (DOUBLE_MANT_DIG + 2))) |
(int128)((a & ((uint128)(-1) >> ((128 + DOUBLE_MANT_DIG + 2) - sd))) != 0);
}
a |= (int128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << DOUBLE_MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= DOUBLE_MANT_DIG - sd;
}
DoubleBits bits = {
.l = ((ulong)(
(((uint)sign & 0x80000000)
| ((e + 1023) << 20))
| ((uint)(a >> 32) & 0x000FFFFF)) << 32)
| (uint)a
};
return bits.d;
}
fn double __floattisf(int128 a) @extname("__floattisf") @weak
{
if (a == 0) return 0.0;
// Grab and remove sign.
int128 sign = a >> 127;
a = (a ^ sign) - sign;
int sd = 128 - (int)$$clz(a);
int e = sd - 1;
if (sd > FLOAT_MANT_DIG)
{
switch (sd)
{
case FLOAT_MANT_DIG + 1:
a <<= 1;
case FLOAT_MANT_DIG + 2:
break;
default:
a = ((uint128)a >> (sd - (FLOAT_MANT_DIG + 2))) |
(int128)((a & ((uint128)(-1) >> ((128 + FLOAT_MANT_DIG + 2) - sd))) != 0);
}
a |= (int128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << FLOAT_MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= FLOAT_MANT_DIG - sd;
}
FloatBits bits = {
.i = (((uint)sign & 0x80000000)
| ((e + 127) << 23))
| ((uint)a & 0x007FFFFF)
};
return bits.f;
}
fn double __floatuntisf(uint128 a) @extname("__floatuntisf") @weak
{
if (a == 0) return 0.0;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
if (sd > FLOAT_MANT_DIG)
if (sd > MANT_DIG)
{
switch (sd)
{
case FLOAT_MANT_DIG + 1:
case MANT_DIG + 1:
a <<= 1;
case FLOAT_MANT_DIG + 2:
case MANT_DIG + 2:
break;
default:
a = (a >> (sd - (FLOAT_MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + FLOAT_MANT_DIG + 2) - sd))) != 0);
a = (a >> (sd - (MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
}
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << FLOAT_MANT_DIG))
if (a & (1i128 << MANT_DIG))
{
a >>= 1;
e++;
@@ -196,35 +133,52 @@ fn double __floatuntisf(uint128 a) @extname("__floatuntisf") @weak
}
else
{
a <<= (FLOAT_MANT_DIG - sd);
a <<= (MANT_DIG - sd);
}
FloatBits bits = {
.i = ((e + 127) << 23) | ((uint)a & 0x007FFFFF)
};
return bits.f;
return bitcast(((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS)) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
}
fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak
private macro float_from_u128($Type, a)
{
if (a == 0) return 0.0;
$switch ($Type):
$case double:
var $Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
const SIGNIFICANT_BITS = 52;
const EXP_BIAS = 1023;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
$case float:
var $Rep = uint;
const MANT_DIG = FLOAT_MANT_DIG;
const EXP_BIAS = 127;
const SIGNIFICANT_BITS = 23;
const MANTISSA_MASK = 0x7F_FFFFu32;
$case float16:
var $Rep = ushort;
const MANT_DIG = HALF_MANT_DIG;
$case float128:
var $Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
$endswitch;
if (a == 0) return ($Type)0;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
if (sd > DOUBLE_MANT_DIG)
if (sd > MANT_DIG)
{
switch (sd)
{
case DOUBLE_MANT_DIG + 1:
case MANT_DIG + 1:
a <<= 1;
case DOUBLE_MANT_DIG + 2:
case MANT_DIG + 2:
break;
default:
a = (a >> (sd - (DOUBLE_MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + DOUBLE_MANT_DIG + 2) - sd))) != 0);
a = (a >> (sd - (MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
}
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << DOUBLE_MANT_DIG))
if (a & (1i128 << MANT_DIG))
{
a >>= 1;
e++;
@@ -232,18 +186,11 @@ fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak
}
else
{
a <<= (DOUBLE_MANT_DIG - sd);
a <<= (MANT_DIG - sd);
}
DoubleBits bits = {
.l = (ulong)(((e + 1023) << 20) | ((uint)(a >> 32) & 0x000FFFFF)) << 32 + (uint)a
};
return bits.d;
return bitcast(((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
}
fn uint128 __fixunsdfti(double a) @extname("__fixunsdfti") = fixuint(a);
fn uint128 __fixunssfti(float a) @extname("__fixunssfti") = fixuint(a);
fn int128 __fixdfti(double a) @extname("__fixdfti") = fixint(a);
fn int128 __fixsfti(float a) @extname("__fixsfti") = fixint(a);
private macro fixuint(a)
{