125 lines
3 KiB
C
125 lines
3 KiB
C
#include <stdbool.h>
|
|
#include <math.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "apfl.h"
|
|
#include "format.h"
|
|
|
|
bool
|
|
apfl_encode_u64(struct apfl_io_writer w, uint_least64_t n)
|
|
{
|
|
unsigned char buf[8] = {
|
|
n & 0xFF,
|
|
(n & 0xFF00) >> 8,
|
|
(n & 0xFF0000) >> 16,
|
|
(n & 0xFF000000) >> 24,
|
|
(n & 0xFF00000000) >> 32,
|
|
(n & 0xFF0000000000) >> 40,
|
|
(n & 0xFF000000000000) >> 48,
|
|
(n & 0xFF00000000000000) >> 56,
|
|
};
|
|
return apfl_io_write_string_view(w, (struct apfl_string_view) {
|
|
.bytes = buf,
|
|
.len = 8,
|
|
});
|
|
}
|
|
|
|
bool
|
|
apfl_decode_u64(struct apfl_io_reader r, uint_least64_t *n)
|
|
{
|
|
unsigned char buf[8];
|
|
FMT_TRY(apfl_io_read_bytes_exact_size(r, buf, 8));
|
|
*n = ((uint_least64_t)buf[0])
|
|
| ((uint_least64_t)buf[1] << 8)
|
|
| ((uint_least64_t)buf[2] << 16)
|
|
| ((uint_least64_t)buf[3] << 24)
|
|
| ((uint_least64_t)buf[4] << 32)
|
|
| ((uint_least64_t)buf[5] << 40)
|
|
| ((uint_least64_t)buf[6] << 48)
|
|
| ((uint_least64_t)buf[7] << 56);
|
|
return true;
|
|
}
|
|
|
|
static uint_least64_t
|
|
double_repr_from_exp_and_frac(uint_least64_t exp, uint_least64_t frac)
|
|
{
|
|
return ((exp & 0x7FF) << 52) | (frac & 0xFFFFFFFFFFFFF);
|
|
}
|
|
|
|
|
|
static uint_least64_t
|
|
prepare_unsigned_double_encode(double d)
|
|
{
|
|
if (d == 0) {
|
|
return 0;
|
|
} else if (isinf(d)) {
|
|
return double_repr_from_exp_and_frac(0x7FF, 0x0);
|
|
} else if (isnan(d)) {
|
|
return double_repr_from_exp_and_frac(0x7FF, 0x8000000000000);
|
|
} else {
|
|
int _exp;
|
|
double _frac = frexp(d, &_exp);
|
|
|
|
_frac *= 9007199254740992; // 2^53
|
|
uint_least64_t frac = (uint_least64_t)(_frac) & 0xFFFFFFFFFFFFF;
|
|
uint_least64_t exp = _exp + 1022;
|
|
|
|
return double_repr_from_exp_and_frac(exp, frac);
|
|
}
|
|
}
|
|
|
|
static double
|
|
double_from_exp_and_frac(uint_least64_t exp, uint_least64_t frac)
|
|
{
|
|
if (exp == 0x7FF) {
|
|
if (frac == 0) {
|
|
return INFINITY;
|
|
} else {
|
|
return NAN;
|
|
}
|
|
} else if (exp == 0) {
|
|
if (frac == 0) {
|
|
return 0;
|
|
} else {
|
|
return 0; // TODO: Subnormal numbers
|
|
}
|
|
} else {
|
|
int iexp = (int)exp - 1022;
|
|
double dfrac = frac | 0x10000000000000;
|
|
dfrac /= 9007199254740992;
|
|
return dfrac * pow(2, (double)iexp);
|
|
}
|
|
}
|
|
|
|
bool
|
|
apfl_encode_double(struct apfl_io_writer w, double d)
|
|
{
|
|
bool negative = copysign(1, d) < 0;
|
|
d = fabs(d);
|
|
|
|
uint_least64_t out = prepare_unsigned_double_encode(d);
|
|
if (negative) {
|
|
out |= 0x8000000000000000;
|
|
}
|
|
|
|
return apfl_encode_u64(w, out);
|
|
}
|
|
|
|
bool
|
|
apfl_decode_double(struct apfl_io_reader r, double *d)
|
|
{
|
|
uint_least64_t n;
|
|
if (!apfl_decode_u64(r, &n)) {
|
|
return false;
|
|
}
|
|
|
|
double sign = (n & ((uint_least64_t)1<<63)) != 0 ? -1 : 1;
|
|
n &= 0x7FFFFFFFFFFFFFFF;
|
|
|
|
uint_least64_t exp = n >> 52;
|
|
uint_least64_t frac = n & 0xFFFFFFFFFFFFF;
|
|
|
|
*d = copysign(double_from_exp_and_frac(exp, frac), sign);
|
|
return true;
|
|
}
|