blob: bcfa6384210a8b07ff04a9d47982fed70b6211c6 [file] [log] [blame]
/*
* Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*
* An internal diy_fp implementation.
* For details, see Loitsch, Florian. "Printing floating-point numbers quickly
* and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.
*/
#ifndef _NJS_DIYFP_H_INCLUDED_
#define _NJS_DIYFP_H_INCLUDED_
typedef struct {
uint64_t significand;
int exp;
} njs_diyfp_t;
#define njs_diyfp(_s, _e) (njs_diyfp_t) \
{ .significand = (_s), .exp = (_e) }
#define njs_uint64(h, l) (((uint64_t) (h) << 32) + (l))
#define NJS_DBL_SIGNIFICAND_SIZE 52
#define NJS_DBL_EXPONENT_BIAS (0x3FF + NJS_DBL_SIGNIFICAND_SIZE)
#define NJS_DBL_EXPONENT_MIN (-NJS_DBL_EXPONENT_BIAS)
#define NJS_DBL_EXPONENT_MAX (0x7FF - NJS_DBL_EXPONENT_BIAS)
#define NJS_DBL_EXPONENT_DENORMAL (-NJS_DBL_EXPONENT_BIAS + 1)
#define NJS_DBL_SIGNIFICAND_MASK njs_uint64(0x000FFFFF, 0xFFFFFFFF)
#define NJS_DBL_HIDDEN_BIT njs_uint64(0x00100000, 0x00000000)
#define NJS_DBL_EXPONENT_MASK njs_uint64(0x7FF00000, 0x00000000)
#define NJS_DIYFP_SIGNIFICAND_SIZE 64
#define NJS_SIGNIFICAND_SIZE 53
#define NJS_SIGNIFICAND_SHIFT (NJS_DIYFP_SIGNIFICAND_SIZE \
- NJS_DBL_SIGNIFICAND_SIZE)
#define NJS_DECIMAL_EXPONENT_OFF 348
#define NJS_DECIMAL_EXPONENT_MIN (-348)
#define NJS_DECIMAL_EXPONENT_MAX 340
#define NJS_DECIMAL_EXPONENT_DIST 8
njs_inline njs_diyfp_t
njs_d2diyfp(double d)
{
int biased_exp;
uint64_t significand;
njs_diyfp_t r;
union {
double d;
uint64_t u64;
} u;
u.d = d;
biased_exp = (u.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE;
significand = u.u64 & NJS_DBL_SIGNIFICAND_MASK;
if (biased_exp != 0) {
r.significand = significand + NJS_DBL_HIDDEN_BIT;
r.exp = biased_exp - NJS_DBL_EXPONENT_BIAS;
} else {
r.significand = significand;
r.exp = NJS_DBL_EXPONENT_MIN + 1;
}
return r;
}
njs_inline double
njs_diyfp2d(njs_diyfp_t v)
{
int exp;
uint64_t significand, biased_exp;
union {
double d;
uint64_t u64;
} u;
exp = v.exp;
significand = v.significand;
while (significand > NJS_DBL_HIDDEN_BIT + NJS_DBL_SIGNIFICAND_MASK) {
significand >>= 1;
exp++;
}
if (exp >= NJS_DBL_EXPONENT_MAX) {
return INFINITY;
}
if (exp < NJS_DBL_EXPONENT_DENORMAL) {
return 0.0;
}
while (exp > NJS_DBL_EXPONENT_DENORMAL
&& (significand & NJS_DBL_HIDDEN_BIT) == 0)
{
significand <<= 1;
exp--;
}
if (exp == NJS_DBL_EXPONENT_DENORMAL
&& (significand & NJS_DBL_HIDDEN_BIT) == 0)
{
biased_exp = 0;
} else {
biased_exp = (uint64_t) (exp + NJS_DBL_EXPONENT_BIAS);
}
u.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK)
| (biased_exp << NJS_DBL_SIGNIFICAND_SIZE);
return u.d;
}
njs_inline njs_diyfp_t
njs_diyfp_shift_left(njs_diyfp_t v, unsigned shift)
{
return njs_diyfp(v.significand << shift, v.exp - shift);
}
njs_inline njs_diyfp_t
njs_diyfp_shift_right(njs_diyfp_t v, unsigned shift)
{
return njs_diyfp(v.significand >> shift, v.exp + shift);
}
njs_inline njs_diyfp_t
njs_diyfp_sub(njs_diyfp_t lhs, njs_diyfp_t rhs)
{
return njs_diyfp(lhs.significand - rhs.significand, lhs.exp);
}
njs_inline njs_diyfp_t
njs_diyfp_mul(njs_diyfp_t lhs, njs_diyfp_t rhs)
{
#if (NJS_HAVE_UNSIGNED_INT128)
uint64_t l, h;
njs_uint128_t u128;
u128 = (njs_uint128_t) (lhs.significand)
* (njs_uint128_t) (rhs.significand);
h = u128 >> 64;
l = (uint64_t) u128;
/* rounding. */
if (l & ((uint64_t) 1 << 63)) {
h++;
}
return njs_diyfp(h, lhs.exp + rhs.exp + 64);
#else
uint64_t a, b, c, d, ac, bc, ad, bd, tmp;
a = lhs.significand >> 32;
b = lhs.significand & 0xffffffff;
c = rhs.significand >> 32;
d = rhs.significand & 0xffffffff;
ac = a * c;
bc = b * c;
ad = a * d;
bd = b * d;
tmp = (bd >> 32) + (ad & 0xffffffff) + (bc & 0xffffffff);
/* mult_round. */
tmp += 1U << 31;
return njs_diyfp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32),
lhs.exp + rhs.exp + 64);
#endif
}
njs_inline njs_diyfp_t
njs_diyfp_normalize(njs_diyfp_t v)
{
return njs_diyfp_shift_left(v, njs_leading_zeros64(v.significand));
}
njs_diyfp_t njs_cached_power_dec(int exp, int *dec_exp);
njs_diyfp_t njs_cached_power_bin(int exp, int *dec_exp);
#endif /* _NJS_DIYFP_H_INCLUDED_ */