feat(stdio): printf family
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,842 @@
|
||||
/* internal unsigned 2^32-limb bignum core for printf float formatting (todo 16)
|
||||
* clean-room implementation; nothing here is exported */
|
||||
/*
|
||||
* Invariants: limbs are little-endian (value = sum d[i] * 2^(32*i)); n == 0
|
||||
* means value 0; no trailing-zero-limb invariant is kept -- callers normalize
|
||||
* via vfpn_bn_norm. Shifts and rounding operate in place; allocation failure
|
||||
* is reported as -1 and leaves errno to __libc_malloc.
|
||||
*/
|
||||
#include "../internal/malloc.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct vfpn_bn
|
||||
{
|
||||
uint32_t fixed[640];
|
||||
uint32_t *d;
|
||||
size_t n;
|
||||
size_t cap;
|
||||
int heap;
|
||||
};
|
||||
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_init(struct vfpn_bn *b)
|
||||
{
|
||||
b->d = b->fixed;
|
||||
b->n = 0;
|
||||
b->cap = 640;
|
||||
b->heap = 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_free(struct vfpn_bn *b)
|
||||
{
|
||||
if (b->heap)
|
||||
{
|
||||
__libc_free(b->d);
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_reserve(struct vfpn_bn *b, size_t need)
|
||||
{
|
||||
if (need <= b->cap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t *new = __libc_malloc(need * sizeof(uint32_t));
|
||||
if (!new)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t copy = (b->cap < need) ? b->cap : need;
|
||||
__builtin_memcpy(new, b->d, copy * sizeof(uint32_t));
|
||||
if (b->heap)
|
||||
{
|
||||
__libc_free(b->d);
|
||||
}
|
||||
|
||||
b->d = new;
|
||||
b->cap = need;
|
||||
b->heap = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_norm(struct vfpn_bn *b)
|
||||
{
|
||||
while (b->n > 0 && b->d[b->n - 1] == 0)
|
||||
{
|
||||
b->n--;
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_set_u64(struct vfpn_bn *b, uint64_t v)
|
||||
{
|
||||
size_t need = 0;
|
||||
for (uint64_t t = v; t != 0; t >>= 32)
|
||||
{
|
||||
need++;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, need) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->n = 0;
|
||||
while (v != 0)
|
||||
{
|
||||
b->d[b->n++] = (uint32_t)v;
|
||||
v >>= 32;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_mul_small(struct vfpn_bn *b, uint32_t m)
|
||||
{
|
||||
if (m == 0)
|
||||
{
|
||||
b->n = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, b->n + 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t carry = 0;
|
||||
for (size_t i = 0; i < b->n; i++)
|
||||
{
|
||||
uint64_t cur = (uint64_t)b->d[i] * m + carry;
|
||||
b->d[i] = (uint32_t)cur;
|
||||
carry = cur >> 32;
|
||||
}
|
||||
|
||||
if (carry != 0)
|
||||
{
|
||||
b->d[b->n++] = (uint32_t)carry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_shl(struct vfpn_bn *b, size_t bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t wl = bits / 32;
|
||||
size_t s = bits % 32;
|
||||
size_t newn = b->n + wl + ((s != 0) ? 1 : 0);
|
||||
if (vfpn_bn_reserve(b, newn) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s == 0)
|
||||
{
|
||||
for (size_t i = b->n; i-- > 0;)
|
||||
{
|
||||
b->d[i + wl] = b->d[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < wl; i++)
|
||||
{
|
||||
b->d[i] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j = newn; j-- > 0;)
|
||||
{
|
||||
uint64_t cur = 0;
|
||||
if (j >= wl && j - wl < b->n)
|
||||
{
|
||||
cur |= (uint64_t)b->d[j - wl] << s;
|
||||
}
|
||||
|
||||
if (j > wl && j - wl - 1 < b->n)
|
||||
{
|
||||
cur |= (uint64_t)b->d[j - wl - 1] >> (32 - s);
|
||||
}
|
||||
|
||||
b->d[j] = (uint32_t)cur;
|
||||
}
|
||||
}
|
||||
|
||||
b->n = newn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_add_one(struct vfpn_bn *b)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < b->n)
|
||||
{
|
||||
b->d[i]++;
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, b->n + 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->d[b->n] = 1;
|
||||
b->n++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_is_zero(const struct vfpn_bn *b)
|
||||
{
|
||||
return b->n == 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_cmp(const struct vfpn_bn *a, const struct vfpn_bn *b)
|
||||
{
|
||||
if (a->n < b->n)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a->n > b->n)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = a->n; i-- > 0;)
|
||||
{
|
||||
if (a->d[i] < b->d[i])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a->d[i] > b->d[i])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_shr_round(struct vfpn_bn *b, size_t bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t w = bits / 32;
|
||||
size_t s = bits % 32;
|
||||
|
||||
if (w >= b->n)
|
||||
{
|
||||
/* Whole value discarded: result is 0, rounded up iff the original
|
||||
* value exceeds 2^(bits-1). That can only happen when bits == 32*n
|
||||
* (s == 0 and w == n), so compare the top limb against half. */
|
||||
int round = 0;
|
||||
if (s == 0 && w == b->n && b->n > 0)
|
||||
{
|
||||
uint32_t top = b->d[b->n - 1];
|
||||
if (top > 0x80000000u)
|
||||
{
|
||||
round = 1;
|
||||
}
|
||||
else if (top == 0x80000000u)
|
||||
{
|
||||
for (size_t i = b->n - 1; i-- > 0;)
|
||||
{
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
round = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b->n = 0;
|
||||
if (round)
|
||||
{
|
||||
return vfpn_bn_add_one(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Round bit and sticky are captured from the discarded low `bits` bits
|
||||
* of the original value before the in-place shift. */
|
||||
int round_bit;
|
||||
int sticky;
|
||||
if (s > 0)
|
||||
{
|
||||
round_bit = (int)((b->d[w] >> (s - 1)) & 1u);
|
||||
sticky = (b->d[w] & ((1u << (s - 1)) - 1u)) != 0;
|
||||
for (size_t i = 0; i < w; i++)
|
||||
{
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
sticky = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
round_bit = (int)((b->d[w - 1] >> 31) & 1u);
|
||||
sticky = (b->d[w - 1] & 0x7FFFFFFFu) != 0;
|
||||
for (size_t i = 0; i + 1 < w; i++)
|
||||
{
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
sticky = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In-place right shift by `bits`; ascending so every source limb is
|
||||
* still readable when its destination is written. */
|
||||
size_t old_n = b->n;
|
||||
if (s == 0)
|
||||
{
|
||||
for (size_t j = 0; j + w < old_n; j++)
|
||||
{
|
||||
b->d[j] = b->d[j + w];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j = 0; j + w < old_n; j++)
|
||||
{
|
||||
uint32_t lo = b->d[j + w] >> s;
|
||||
uint32_t hi = (j + w + 1 < old_n) ? (b->d[j + w + 1] << (32 - s)) : 0;
|
||||
b->d[j] = lo | hi;
|
||||
}
|
||||
}
|
||||
|
||||
b->n = old_n - w;
|
||||
vfpn_bn_norm(b);
|
||||
|
||||
int odd = (b->n > 0 && (b->d[0] & 1u)) ? 1 : 0;
|
||||
if (round_bit && (sticky || odd))
|
||||
{
|
||||
return vfpn_bn_add_one(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) uint32_t
|
||||
vfpn_bn_divmod_small_1e9(struct vfpn_bn *b)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
for (size_t i = b->n; i-- > 0;)
|
||||
{
|
||||
uint64_t cur = (r << 32) | b->d[i];
|
||||
b->d[i] = (uint32_t)(cur / 1000000000ULL);
|
||||
r = cur % 1000000000ULL;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(b);
|
||||
return (uint32_t)r;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_pow5(struct vfpn_bn *b, size_t k)
|
||||
{
|
||||
int r = vfpn_bn_set_u64(b, 1);
|
||||
if (r != 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < k; i++)
|
||||
{
|
||||
r = vfpn_bn_mul_small(b, 5);
|
||||
if (r != 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- exact decimal rounding layer ------------------------------------ */
|
||||
|
||||
/*
|
||||
* Number of bits needed to hold |b| (index of the highest set bit plus one);
|
||||
* 0 for the value zero. The caller may leave a trailing zero limb in place,
|
||||
* so the scan starts from the recorded length and skips down.
|
||||
*/
|
||||
static __attribute__((unused)) size_t
|
||||
vfpn_bn_bitlen(const struct vfpn_bn *b)
|
||||
{
|
||||
if (b->n == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t i = b->n - 1;
|
||||
while (i > 0 && b->d[i] == 0)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
|
||||
if (b->d[i] == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return i * 32 + (size_t)(32 - __builtin_clz(b->d[i]));
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_get_bit(const struct vfpn_bn *b, size_t i)
|
||||
{
|
||||
return (int)((b->d[i / 32] >> (i % 32)) & 1u);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set bit i of b. Zero-extends the limb array when the bit lies beyond the
|
||||
* current length; ORing never clears, so the value only grows.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_set_bit(struct vfpn_bn *b, size_t i, int bit)
|
||||
{
|
||||
if (!bit)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b->n <= i / 32)
|
||||
{
|
||||
size_t need = i / 32 + 1;
|
||||
if (vfpn_bn_reserve(b, need) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t j = b->n; j < need; j++)
|
||||
{
|
||||
b->d[j] = 0;
|
||||
}
|
||||
|
||||
b->n = need;
|
||||
}
|
||||
|
||||
b->d[i / 32] |= (1u << (i % 32));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shift |b| left by one bit in place (limb ripple). The caller's cap must
|
||||
* already make room for the possible extra top limb; the reserve here is
|
||||
* only a safety net and normally returns immediately.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_shl1(struct vfpn_bn *b)
|
||||
{
|
||||
if (b->n == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, b->n + 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t carry = 0;
|
||||
for (size_t i = 0; i < b->n; i++)
|
||||
{
|
||||
uint32_t cur = b->d[i];
|
||||
b->d[i] = (cur << 1) | carry;
|
||||
carry = cur >> 31;
|
||||
}
|
||||
|
||||
if (carry != 0)
|
||||
{
|
||||
b->d[b->n++] = carry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In-place limb-wise subtraction a -= b. The caller guarantees a >= b, so
|
||||
* the borrow always clears before the top of a; a is renormalized.
|
||||
*/
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_sub_inplace(struct vfpn_bn *a, const struct vfpn_bn *b)
|
||||
{
|
||||
uint32_t borrow = 0;
|
||||
size_t i = 0;
|
||||
for (; i < b->n; i++)
|
||||
{
|
||||
uint64_t diff = (uint64_t)a->d[i] - (uint64_t)b->d[i] - borrow;
|
||||
a->d[i] = (uint32_t)diff;
|
||||
borrow = (uint32_t)((diff >> 32) & 1u);
|
||||
}
|
||||
|
||||
while (i < a->n && borrow != 0)
|
||||
{
|
||||
a->d[i]--;
|
||||
borrow = (a->d[i] == 0xFFFFFFFFu) ? 1u : 0u;
|
||||
i++;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(a);
|
||||
}
|
||||
|
||||
/*
|
||||
* Q = N / D, R = N mod D by restoring binary long division. N may be zero;
|
||||
* D must be nonzero. Q and R are written over whatever they held. The
|
||||
* quotients bits are produced most-significant first; the loop runs over the
|
||||
* dividend's bit positions from bitlen(N)-1 down to 0, pulling one new
|
||||
* dividend bit into R and subtracting D whenever R >= D.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_divmod_binary(struct vfpn_bn *Q, struct vfpn_bn *R, const struct vfpn_bn *N,
|
||||
const struct vfpn_bn *D)
|
||||
{
|
||||
if (vfpn_bn_is_zero(D))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(Q, N->n + 2) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(R, N->n + 2) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Q->n = 0;
|
||||
R->n = 0;
|
||||
|
||||
size_t nbits = vfpn_bn_bitlen(N);
|
||||
for (size_t i = nbits; i-- > 0;)
|
||||
{
|
||||
if (vfpn_bn_shl1(R) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_set_bit(R, 0, vfpn_bn_get_bit(N, i)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_cmp(R, D) >= 0)
|
||||
{
|
||||
vfpn_bn_sub_inplace(R, D);
|
||||
if (vfpn_bn_set_bit(Q, i, 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vfpn_bn_norm(Q);
|
||||
vfpn_bn_norm(R);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare 2*a against b (both canonical base-2^32 digit strings) without
|
||||
* building the doubled value: doubling a limb's carry is the next limb's
|
||||
* (d[i] >> 31), which the loop below folds in, and an extra top digit
|
||||
* appears when the original top limb had its high bit set. All digits of 2*a
|
||||
* stay below 2^32, so a plain digitwise comparison is exact.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_cmp_doubled(const struct vfpn_bn *a, const struct vfpn_bn *b)
|
||||
{
|
||||
if (a->n == 0)
|
||||
{
|
||||
return vfpn_bn_is_zero(b) ? 0 : -1;
|
||||
}
|
||||
|
||||
size_t extra = (a->d[a->n - 1] >> 31) ? 1u : 0u;
|
||||
size_t la = a->n + extra;
|
||||
if (la != b->n)
|
||||
{
|
||||
return (la > b->n) ? 1 : -1;
|
||||
}
|
||||
|
||||
for (size_t i = la; i-- > 0;)
|
||||
{
|
||||
uint32_t da;
|
||||
if (extra != 0 && i == a->n)
|
||||
{
|
||||
da = 1; /* carry out of the doubled top limb */
|
||||
}
|
||||
else
|
||||
{
|
||||
da = (uint32_t)(a->d[i] << 1);
|
||||
if (i > 0)
|
||||
{
|
||||
da += (a->d[i - 1] >> 31);
|
||||
}
|
||||
}
|
||||
|
||||
if (da != b->d[i])
|
||||
{
|
||||
return (da < b->d[i]) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* R = round_half_even(mant * 2^exp2 * 10^s), computed exactly as an integer
|
||||
* bignum (mant > 0; s may be negative). s >= 0 scales R by 5^s and by a
|
||||
* single power of two, rounding once at the end (nearest-even). s < 0 builds
|
||||
* D = 5^(-s) and shifts either N or D so that value = N / D, divides exactly,
|
||||
* and rounds the quotient half-even off the remainder: 2*rem > D rounds up,
|
||||
* 2*rem == D rounds up only when the quotient is odd. Returns 0 or -1 on
|
||||
* allocation failure (R is then unspecified).
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_round_scale10(struct vfpn_bn *R, uint64_t mant, int exp2, long s)
|
||||
{
|
||||
struct vfpn_bn N;
|
||||
struct vfpn_bn D;
|
||||
struct vfpn_bn Q;
|
||||
struct vfpn_bn rem;
|
||||
vfpn_bn_init(&N);
|
||||
vfpn_bn_init(&D);
|
||||
vfpn_bn_init(&Q);
|
||||
vfpn_bn_init(&rem);
|
||||
|
||||
if (s >= 0)
|
||||
{
|
||||
if (vfpn_bn_set_u64(R, mant) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (long i = 0; i < s; i++)
|
||||
{
|
||||
if (vfpn_bn_mul_small(R, 5) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
long long sh = (long long)exp2 + (long long)s;
|
||||
if (sh >= 0)
|
||||
{
|
||||
if (vfpn_bn_shl(R, (size_t)sh) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vfpn_bn_shr_round(R, (size_t)(-sh)) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
vfpn_bn_free(&N);
|
||||
vfpn_bn_free(&D);
|
||||
vfpn_bn_free(&Q);
|
||||
vfpn_bn_free(&rem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* value = mant * 2^exp2 / (2^t * 5^t) with t = -s: make it N / D. */
|
||||
{
|
||||
long long t = -(long long)s;
|
||||
long long sh = (long long)exp2 - t;
|
||||
|
||||
if (vfpn_bn_pow5(&D, (size_t)t) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (vfpn_bn_set_u64(&N, mant) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sh >= 0)
|
||||
{
|
||||
if (vfpn_bn_shl(&N, (size_t)sh) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(&N);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vfpn_bn_shl(&D, (size_t)(-sh)) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(&D);
|
||||
}
|
||||
|
||||
if (vfpn_divmod_binary(&Q, &rem, &N, &D) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int c = vfpn_bn_cmp_doubled(&rem, &D);
|
||||
if (c > 0 || (c == 0 && Q.n > 0 && (Q.d[0] & 1u) != 0))
|
||||
{
|
||||
if (vfpn_bn_add_one(&Q) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(R, Q.n) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (Q.n > 0)
|
||||
{
|
||||
__builtin_memcpy(R->d, Q.d, Q.n * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
R->n = Q.n;
|
||||
}
|
||||
|
||||
vfpn_bn_free(&N);
|
||||
vfpn_bn_free(&D);
|
||||
vfpn_bn_free(&Q);
|
||||
vfpn_bn_free(&rem);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
vfpn_bn_free(&N);
|
||||
vfpn_bn_free(&D);
|
||||
vfpn_bn_free(&Q);
|
||||
vfpn_bn_free(&rem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decimal digits of |v| into out (most significant first, no terminating
|
||||
* NUL); returns the digit count or -1 when outcap is too small. The value
|
||||
* zero yields a single '0'. Groups of nine decimal digits are stripped from
|
||||
* the low end into a fixed stack (a 640-limb value caps at ~6165 digits, far
|
||||
* inside 700 groups), then re-emitted top group unpadded, lower groups
|
||||
* zero-padded to nine.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_to_dec(const struct vfpn_bn *v, char *out, size_t outcap)
|
||||
{
|
||||
if (vfpn_bn_is_zero(v))
|
||||
{
|
||||
if (outcap < 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
out[0] = '0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct vfpn_bn w;
|
||||
vfpn_bn_init(&w);
|
||||
if (vfpn_bn_reserve(&w, v->n) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
w.n = v->n;
|
||||
if (v->n > 0)
|
||||
{
|
||||
__builtin_memcpy(w.d, v->d, v->n * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
uint32_t groups[700];
|
||||
size_t ngroups = 0;
|
||||
while (!vfpn_bn_is_zero(&w))
|
||||
{
|
||||
if (ngroups >= 700)
|
||||
{
|
||||
vfpn_bn_free(&w);
|
||||
return -1;
|
||||
}
|
||||
|
||||
groups[ngroups++] = vfpn_bn_divmod_small_1e9(&w);
|
||||
}
|
||||
|
||||
vfpn_bn_free(&w);
|
||||
|
||||
size_t top = ngroups - 1;
|
||||
uint32_t t = groups[top];
|
||||
size_t tdig = 1;
|
||||
for (uint32_t x = t / 10; x != 0; x /= 10)
|
||||
{
|
||||
tdig++;
|
||||
}
|
||||
|
||||
size_t ndigits = tdig + (ngroups - 1) * 9;
|
||||
if (outcap < ndigits)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
char tmp[10];
|
||||
size_t ntmp = 0;
|
||||
do
|
||||
{
|
||||
tmp[ntmp++] = (char)('0' + t % 10);
|
||||
t /= 10;
|
||||
} while (t != 0);
|
||||
|
||||
while (ntmp > 0)
|
||||
{
|
||||
out[pos++] = tmp[--ntmp];
|
||||
}
|
||||
|
||||
for (size_t gi = ngroups - 1; gi-- > 0;)
|
||||
{
|
||||
uint32_t val = groups[gi];
|
||||
for (int k = 8; k >= 0; k--)
|
||||
{
|
||||
out[pos + (size_t)k] = (char)('0' + val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
pos += 9;
|
||||
}
|
||||
|
||||
return (int)ndigits;
|
||||
}
|
||||
Reference in New Issue
Block a user