void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) { uint32_t status = 0; if (!mpd_qcopy(result, a, &status)) { mpd_addstatus_raise(ctx, status); } }
/* Round a number to prec digits. The adjusted exponent stays the same or increases by one if rounding up crosses a power of ten boundary. If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation is set and the result is NaN. */ static inline void _mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec, const mpd_context_t *ctx, uint32_t *status) { mpd_ssize_t exp = a->exp + a->digits - prec; if (prec <= 0) { mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */ return; /* GCOV_NOT_REACHED */ } if (mpd_isspecial(a) || mpd_iszero(a)) { mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */ return; /* GCOV_NOT_REACHED */ } mpd_qrescale_fmt(result, a, exp, ctx, status); if (result->digits > prec) { mpd_qrescale_fmt(result, result, exp+1, ctx, status); } }
/* * Return the string representation of an mpd_t, formatted according to 'spec'. * The format specification is assumed to be valid. Memory errors are indicated * as usual. This function is quiet. */ char * mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status) { mpd_uint_t dt[MPD_MINALLOC_MAX]; mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt}; mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE; mpd_mbstr_t result; mpd_spec_t stackspec; char type = spec->type; int flags = 0; if (spec->min_width > MPD_MAX_PREC) { *status |= MPD_Invalid_operation; return NULL; } if (isupper((uchar)type)) { type = tolower((uchar)type); flags |= MPD_FMT_UPPER; } if (spec->sign == ' ') { flags |= MPD_FMT_SIGN_SPACE; } else if (spec->sign == '+') { flags |= MPD_FMT_SIGN_PLUS; } if (mpd_isspecial(dec)) { if (spec->align == 'z') { stackspec = *spec; stackspec.fill[0] = ' '; stackspec.fill[1] = '\0'; stackspec.align = '>'; spec = &stackspec; } } else { uint32_t workstatus = 0; mpd_ssize_t prec; switch (type) { case 'g': flags |= MPD_FMT_TOSCI; break; case 'e': flags |= MPD_FMT_EXP; break; case '%': flags |= MPD_FMT_PERCENT; if (!mpd_qcopy(&tmp, dec, status)) { return NULL; } tmp.exp += 2; dec = &tmp; type = 'f'; /* fall through */ case 'f': flags |= MPD_FMT_FIXED; break; default: abort(); /* debug: GCOV_NOT_REACHED */ } if (spec->prec >= 0) { if (spec->prec > MPD_MAX_PREC) { *status |= MPD_Invalid_operation; goto error; } switch (type) { case 'g': prec = (spec->prec == 0) ? 1 : spec->prec; if (dec->digits > prec) { _mpd_round(&tmp, dec, prec, ctx, &workstatus); dec = &tmp; } break; case 'e': if (mpd_iszero(dec)) { dplace = 1-spec->prec; } else { _mpd_round(&tmp, dec, spec->prec+1, ctx, &workstatus); dec = &tmp; } break; case 'f': mpd_qrescale(&tmp, dec, -spec->prec, ctx, &workstatus); dec = &tmp; break; } } if (type == 'f') { if (mpd_iszero(dec) && dec->exp > 0) { mpd_qrescale(&tmp, dec, 0, ctx, &workstatus); dec = &tmp; } } if (workstatus&MPD_Errors) { *status |= (workstatus&MPD_Errors); goto error; } } /* * At this point, for all scaled or non-scaled decimals: * 1) 1 <= digits <= MAX_PREC+1 * 2) adjexp(scaled) = adjexp(orig) [+1] * 3) case 'g': MIN_ETINY <= exp <= MAX_EMAX+1 * case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1 * case 'f': MIN_ETINY <= exp <= MAX_EMAX+1 * 4) max memory alloc in _mpd_to_string: * case 'g': MAX_PREC+36 * case 'e': MAX_PREC+36 * case 'f': 2*MPD_MAX_PREC+30 */ result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace); result.nchars = result.nbytes; if (result.nbytes < 0) { *status |= MPD_Malloc_error; goto error; } if (*spec->dot != '\0' && !mpd_isspecial(dec)) { if (result.nchars > MPD_MAX_PREC+36) { /* Since a group length of one is not explicitly * disallowed, ensure that it is always possible to * insert a four byte separator after each digit. */ *status |= MPD_Invalid_operation; mpd_free(result.data); goto error; } if (!_mpd_apply_lconv(&result, spec, status)) { goto error; } } if (spec->min_width) { if (!_mpd_add_pad(&result, spec, status)) { goto error; } } mpd_del(&tmp); return result.data; error: mpd_del(&tmp); return NULL; }