/* The faster in-place functions are tested against std_trans(). */ static int testit_uchar(void (* func)(uint8_t *, mpd_size_t, mpd_size_t), mpd_size_t rows, mpd_size_t cols) { uint8_t *a = NULL, *src = NULL, *dest = NULL; clock_t start_fast, end_fast, start_std, end_std; mpd_size_t msize; mpd_size_t i; int ret = 1; msize = mul_size_t(rows, cols); if ((a = mpd_alloc(msize, sizeof *a)) == NULL) { goto error; } if ((src = mpd_alloc(msize, sizeof *src)) == NULL) { goto error; } if ((dest = mpd_alloc(msize, sizeof *dest)) == NULL) { goto error; } for (i = 0; i < msize; i++) { a[i] = src[i] = random(); } start_std= clock(); std_trans_c(dest, src, rows, cols); end_std= clock(); start_fast = clock(); func(a, rows, cols); end_fast = clock(); for (i = 0; i < msize; i++) { if (a[i] != dest[i]) { fprintf(stderr, "FAIL: a[%"PRI_mpd_size_t"] = %d\t" "dest[%"PRI_mpd_size_t"] = %d\n", i, a[i], i, dest[i]); exit(1); } } fprintf(stderr, "size: %10"PRI_mpd_size_t"\tstd_trans: %6.2f sec\t " "in_place_trans: %6.2f sec\n", msize, (double)(end_std-start_std)/(double)CLOCKS_PER_SEC, (double)(end_fast-start_fast)/(double)CLOCKS_PER_SEC); out: if (a) __mingw_dfp_get_globals()->mpd_free(a); if (src) __mingw_dfp_get_globals()->mpd_free(src); if (dest) __mingw_dfp_get_globals()->mpd_free(dest); return ret; error: ret = 0; goto out; }
/* * Convert a numeric-string to its locale-specific appearance. * The string must have one of these forms: * * 1) [sign] digits [exponent-part] * 2) [sign] digits '.' [digits] [exponent-part] * * Not allowed, since _mpd_to_string() never returns this form: * * 3) [sign] '.' digits [exponent-part] * * Input: result->data := original numeric string (ASCII) * result->bytes := strlen(result->data) * result->nchars := strlen(result->data) * * Output: result->data := modified or original string * result->bytes := strlen(result->data) * result->nchars := number of characters (possibly UTF-8) */ static int _mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) { const char *sign = NULL, *intpart = NULL, *dot = NULL; const char *rest, *dp; char *decstring; mpd_ssize_t n_int, n_rest; /* original numeric string */ dp = result->data; /* sign */ if (*dp == '+' || *dp == '-' || *dp == ' ') { sign = dp++; } /* integer part */ assert(isdigit((uchar)*dp)); intpart = dp++; while (isdigit((uchar)*dp)) { dp++; } n_int = (mpd_ssize_t)(dp-intpart); /* decimal point */ if (*dp == '.') { dp++; dot = spec->dot; } /* rest */ rest = dp; n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data); if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) { /* _mpd_add_sep_dot() would not change anything */ return 1; } /* Determine the size of the new decimal string after inserting the * decimal point, optional separators and optional padding. */ decstring = result->data; result->data = NULL; _mpd_add_sep_dot(result, sign, intpart, n_int, dot, rest, n_rest, spec); result->data = mpd_alloc(result->nbytes+1, 1); if (result->data == NULL) { *status |= MPD_Malloc_error; mpd_free(decstring); return 0; } /* Perform actual writes. */ _mpd_add_sep_dot(result, sign, intpart, n_int, dot, rest, n_rest, spec); mpd_free(decstring); return 1; }
/* * Knuth, TAOCP Volume 2, 4.3.1: * q, r := quotient and remainder of uconst (len nplusm) * divided by vconst (len n) * nplusm >= n * * If r is not NULL, r will contain the remainder. If r is NULL, the * return value indicates if there is a remainder: 1 for true, 0 for * false. A return value of -1 indicates an error. */ int _mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, const mpd_uint_t *uconst, const mpd_uint_t *vconst, mpd_size_t nplusm, mpd_size_t n) { mpd_uint_t ustatic[MPD_MINALLOC_MAX]; mpd_uint_t vstatic[MPD_MINALLOC_MAX]; mpd_uint_t *u = ustatic; mpd_uint_t *v = vstatic; mpd_uint_t d, qhat, rhat, w2[2]; mpd_uint_t hi, lo, x; mpd_uint_t carry; mpd_size_t i, j, m; int retval = 0; assert(n > 1 && nplusm >= n); m = sub_size_t(nplusm, n); /* D1: normalize */ d = MPD_RADIX / (vconst[n-1] + 1); if (nplusm >= MPD_MINALLOC_MAX) { if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) { return -1; } } if (n >= MPD_MINALLOC_MAX) { if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) { mpd_free(u); return -1; } } _mpd_shortmul(u, uconst, nplusm, d); _mpd_shortmul(v, vconst, n, d); /* D2: loop */ for (j=m; j != MPD_SIZE_MAX; j--) { /* D3: calculate qhat and rhat */ rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]); qhat = w2[1] * MPD_RADIX + w2[0]; while (1) { if (qhat < MPD_RADIX) { _mpd_singlemul(w2, qhat, v[n-2]); if (w2[1] <= rhat) { if (w2[1] != rhat || w2[0] <= u[j+n-2]) { break; } } } qhat -= 1; rhat += v[n-1]; if (rhat < v[n-1] || rhat >= MPD_RADIX) { break; } } /* D4: multiply and subtract */ carry = 0; for (i=0; i <= n; i++) { _mpd_mul_words(&hi, &lo, qhat, v[i]); lo = carry + lo; if (lo < carry) hi++; _mpd_div_words_r(&hi, &lo, hi, lo); x = u[i+j] - lo; carry = (u[i+j] < x); u[i+j] = carry ? x+MPD_RADIX : x; carry += hi; } q[j] = qhat; /* D5: test remainder */ if (carry) { q[j] -= 1; /* D6: add back */ (void)_mpd_baseadd(u+j, u+j, v, n+1, n); } } /* D8: unnormalize */ if (r != NULL) { _mpd_shortdiv(r, u, n, d); /* we are not interested in the return value here */ retval = 0; } else { retval = !_mpd_isallzero(u, n); } if (u != ustatic) mpd_free(u); if (v != vstatic) mpd_free(v); return retval; }
/* * Set *result to the string representation of a decimal. Return the length * of *result, not including the terminating '\0' character. * * Formatting is done according to 'flags'. A return value of -1 with *result * set to NULL indicates MPD_Malloc_error. * * 'dplace' is the default place of the decimal point. It is always set to * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP. */ static mpd_ssize_t _mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace) { char *decstring = NULL, *cp = NULL; mpd_ssize_t ldigits; mpd_ssize_t mem = 0, k; if (mpd_isspecial(dec)) { mem = sizeof "-Infinity"; if (mpd_isnan(dec) && dec->len > 0) { /* diagnostic code */ mem += dec->digits; } cp = decstring = mpd_alloc(mem, sizeof *decstring); if (cp == NULL) { *result = NULL; return -1; } if (mpd_isnegative(dec)) { *cp++ = '-'; } else if (flags&MPD_FMT_SIGN_SPACE) { *cp++ = ' '; } else if (flags&MPD_FMT_SIGN_PLUS) { *cp++ = '+'; } if (mpd_isnan(dec)) { if (mpd_isqnan(dec)) { strcpy(cp, "NaN"); cp += 3; } else { strcpy(cp, "sNaN"); cp += 4; } if (dec->len > 0) { /* diagnostic code */ cp = coeff_to_string(cp, dec); } } else if (mpd_isinfinite(dec)) { strcpy(cp, "Infinity"); cp += 8; } else { /* debug */ abort(); /* GCOV_NOT_REACHED */ } } else { assert(dec->len > 0); /* * For easier manipulation of the decimal point's location * and the exponent that is finally printed, the number is * rescaled to a virtual representation with exp = 0. Here * ldigits denotes the number of decimal digits to the left * of the decimal point and remains constant once initialized. * * dplace is the location of the decimal point relative to * the start of the coefficient. Note that 3) always holds * when dplace is shifted. * * 1) ldigits := dec->digits - dec->exp * 2) dplace := ldigits (initially) * 3) exp := ldigits - dplace (initially exp = 0) * * 0.00000_.____._____000000. * ^ ^ ^ ^ * | | | | * | | | `- dplace >= digits * | | `- dplace in the middle of the coefficient * | ` dplace = 1 (after the first coefficient digit) * `- dplace <= 0 */ ldigits = dec->digits + dec->exp; if (flags&MPD_FMT_EXP) { ; } else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) { /* MPD_FMT_FIXED: always use fixed point notation. * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range, * override exponent notation. */ dplace = ldigits; } else if (flags&MPD_FMT_TOENG) { if (mpd_iszero(dec)) { /* If the exponent is divisible by three, * dplace = 1. Otherwise, move dplace one * or two places to the left. */ dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3); } else { /* ldigits-1 is the adjusted exponent, which * should be divisible by three. If not, move * dplace one or two places to the right. */ dplace += mod_mpd_ssize_t(ldigits-1, 3); } } /* * Basic space requirements: * * [-][.][coeffdigits][E][-][expdigits+1][%]['\0'] * * If the decimal point lies outside of the coefficient digits, * space is adjusted accordingly. */ if (dplace <= 0) { mem = -dplace + dec->digits + 2; } else if (dplace >= dec->digits) { mem = dplace; } else { mem = dec->digits; } mem += (MPD_EXPDIGITS+1+6); cp = decstring = mpd_alloc(mem, sizeof *decstring); if (cp == NULL) { *result = NULL; return -1; } if (mpd_isnegative(dec)) { *cp++ = '-'; } else if (flags&MPD_FMT_SIGN_SPACE) { *cp++ = ' '; } else if (flags&MPD_FMT_SIGN_PLUS) { *cp++ = '+'; } if (dplace <= 0) { /* space: -dplace+dec->digits+2 */ *cp++ = '0'; *cp++ = '.'; for (k = 0; k < -dplace; k++) { *cp++ = '0'; } cp = coeff_to_string(cp, dec); } else if (dplace >= dec->digits) { /* space: dplace */ cp = coeff_to_string(cp, dec); for (k = 0; k < dplace-dec->digits; k++) { *cp++ = '0'; } } else { /* space: dec->digits+1 */ cp = coeff_to_string_dot(cp, cp+dplace, dec); } /* * Conditions for printing an exponent: * * MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace * MPD_FMT_FIXED: never (ldigits == dplace) * MPD_FMT_EXP: always */ if (ldigits != dplace || flags&MPD_FMT_EXP) { /* space: expdigits+2 */ *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e'; cp = exp_to_string(cp, ldigits-dplace); } if (flags&MPD_FMT_PERCENT) { *cp++ = '%'; } } assert(cp < decstring+mem); assert(cp-decstring < MPD_SSIZE_MAX); *cp = '\0'; *result = decstring; return (mpd_ssize_t)(cp-decstring); }