#include #include #include #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; }