📄 numeric.c
字号:
/* Select display scale */ res_dscale = NUMERIC_MIN_SIG_DIGITS - qweight; res_dscale = Max(res_dscale, var1->dscale); res_dscale = Max(res_dscale, var2->dscale); res_dscale = Max(res_dscale, NUMERIC_MIN_DISPLAY_SCALE); res_dscale = Min(res_dscale, NUMERIC_MAX_DISPLAY_SCALE); /* Select result scale */ *rscale = res_rscale = res_dscale + 4; return res_dscale;}intPGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result){ NumericDigit *res_digits; int res_ndigits; int res_sign; int res_weight; numeric dividend; numeric divisor[10]; int ndigits_tmp; int weight_tmp; int rscale_tmp; int ri; int i; long guess; long first_have; long first_div; int first_nextdigit; int stat = 0; int rscale; int res_dscale = select_div_scale(var1, var2, &rscale); int err = -1; NumericDigit *tmp_buf; /* * First of all division by zero check */ ndigits_tmp = var2->ndigits + 1; if (ndigits_tmp == 1) { errno = PGTYPES_NUM_DIVIDE_ZERO; return -1; } /* * Determine the result sign, weight and number of digits to calculate */ if (var1->sign == var2->sign) res_sign = NUMERIC_POS; else res_sign = NUMERIC_NEG; res_weight = var1->weight - var2->weight + 1; res_ndigits = rscale + res_weight; if (res_ndigits <= 0) res_ndigits = 1; /* * Now result zero check */ if (var1->ndigits == 0) { zero_var(result); result->rscale = rscale; return 0; } /* * Initialize local variables */ init_var(÷nd); for (i = 1; i < 10; i++) init_var(&divisor[i]); /* * Make a copy of the divisor which has one leading zero digit */ divisor[1].ndigits = ndigits_tmp; divisor[1].rscale = var2->ndigits; divisor[1].sign = NUMERIC_POS; divisor[1].buf = digitbuf_alloc(ndigits_tmp); if (divisor[1].buf == NULL) goto done; divisor[1].digits = divisor[1].buf; divisor[1].digits[0] = 0; memcpy(&(divisor[1].digits[1]), var2->digits, ndigits_tmp - 1); /* * Make a copy of the dividend */ dividend.ndigits = var1->ndigits; dividend.weight = 0; dividend.rscale = var1->ndigits; dividend.sign = NUMERIC_POS; dividend.buf = digitbuf_alloc(var1->ndigits); if (dividend.buf == NULL) goto done; dividend.digits = dividend.buf; memcpy(dividend.digits, var1->digits, var1->ndigits); /* * Setup the result. Do the allocation in a temporary buffer first, so we * don't free result->buf unless we have successfully allocated a buffer * to replace it with. */ tmp_buf = digitbuf_alloc(res_ndigits + 2); if (tmp_buf == NULL) goto done; digitbuf_free(result->buf); result->buf = tmp_buf; res_digits = result->buf; result->digits = res_digits; result->ndigits = res_ndigits; result->weight = res_weight; result->rscale = rscale; result->sign = res_sign; res_digits[0] = 0; first_div = divisor[1].digits[1] * 10; if (ndigits_tmp > 2) first_div += divisor[1].digits[2]; first_have = 0; first_nextdigit = 0; weight_tmp = 1; rscale_tmp = divisor[1].rscale; for (ri = 0; ri <= res_ndigits; ri++) { first_have = first_have * 10; if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits) first_have += dividend.digits[first_nextdigit]; first_nextdigit++; guess = (first_have * 10) / first_div + 1; if (guess > 9) guess = 9; while (guess > 0) { if (divisor[guess].buf == NULL) { int i; long sum = 0; memcpy(&divisor[guess], &divisor[1], sizeof(numeric)); divisor[guess].buf = digitbuf_alloc(divisor[guess].ndigits); if (divisor[guess].buf == NULL) goto done; divisor[guess].digits = divisor[guess].buf; for (i = divisor[1].ndigits - 1; i >= 0; i--) { sum += divisor[1].digits[i] * guess; divisor[guess].digits[i] = sum % 10; sum /= 10; } } divisor[guess].weight = weight_tmp; divisor[guess].rscale = rscale_tmp; stat = cmp_abs(÷nd, &divisor[guess]); if (stat >= 0) break; guess--; } res_digits[ri + 1] = guess; if (stat == 0) { ri++; break; } weight_tmp--; rscale_tmp++; if (guess == 0) continue; if (sub_abs(÷nd, &divisor[guess], ÷nd) != 0) goto done; first_nextdigit = dividend.weight - weight_tmp; first_have = 0; if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits) first_have = dividend.digits[first_nextdigit]; first_nextdigit++; } result->ndigits = ri + 1; if (ri == res_ndigits + 1) { int carry = (res_digits[ri] > 4) ? 1 : 0; result->ndigits = ri; res_digits[ri] = 0; while (carry && ri > 0) { carry += res_digits[--ri]; res_digits[ri] = carry % 10; carry /= 10; } } while (result->ndigits > 0 && *(result->digits) == 0) { (result->digits)++; (result->weight)--; (result->ndigits)--; } while (result->ndigits > 0 && result->digits[result->ndigits - 1] == 0) (result->ndigits)--; if (result->ndigits == 0) result->sign = NUMERIC_POS; result->dscale = res_dscale; err = 0; /* if we've made it this far, return success */done: /* * Tidy up */ if (dividend.buf != NULL) digitbuf_free(dividend.buf); for (i = 1; i < 10; i++) { if (divisor[i].buf != NULL) digitbuf_free(divisor[i].buf); } return err;}intPGTYPESnumeric_cmp(numeric *var1, numeric *var2){ /* use cmp_abs function to calculate the result */ /* both are positive: normal comparation with cmp_abs */ if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_POS) return cmp_abs(var1, var2); /* both are negative: return the inverse of the normal comparation */ if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_NEG) { /* * instead of inverting the result, we invert the paramter ordering */ return cmp_abs(var2, var1); } /* one is positive, one is negative: trivial */ if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_NEG) return 1; if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_POS) return -1; errno = PGTYPES_NUM_BAD_NUMERIC; return INT_MAX;}intPGTYPESnumeric_from_int(signed int int_val, numeric *var){ /* implicit conversion */ signed long int long_int = int_val; return PGTYPESnumeric_from_long(long_int, var);}intPGTYPESnumeric_from_long(signed long int long_val, numeric *var){ /* calculate the size of the long int number */ /* a number n needs log_10 n digits */ /* * however we multiply by 10 each time and compare instead of calculating * the logarithm */ int size = 0; int i; signed long int abs_long_val = long_val; signed long int extract; signed long int reach_limit; if (abs_long_val < 0) { abs_long_val *= -1; var->sign = NUMERIC_NEG; } else var->sign = NUMERIC_POS; reach_limit = 1; do { size++; reach_limit *= 10; } while (reach_limit - 1 < abs_long_val && reach_limit <= LONG_MAX / 10); if (reach_limit > LONG_MAX / 10) { /* add the first digit and a .0 */ size += 2; } else { /* always add a .0 */ size++; reach_limit /= 10; } if (alloc_var(var, size) < 0) return -1; var->rscale = 1; var->dscale = 1; var->weight = size - 2; i = 0; do { extract = abs_long_val - (abs_long_val % reach_limit); var->digits[i] = extract / reach_limit; abs_long_val -= extract; i++; reach_limit /= 10; /* * we can abandon if abs_long_val reaches 0, because the memory is * initialized properly and filled with '0', so converting 10000 in * only one step is no problem */ } while (abs_long_val > 0); return 0;}intPGTYPESnumeric_copy(numeric *src, numeric *dst){ int i; if (dst == NULL) return -1; zero_var(dst); dst->weight = src->weight; dst->rscale = src->rscale; dst->dscale = src->dscale; dst->sign = src->sign; if (alloc_var(dst, src->ndigits) != 0) return -1; for (i = 0; i < src->ndigits; i++) dst->digits[i] = src->digits[i]; return 0;}intPGTYPESnumeric_from_double(double d, numeric *dst){ char buffer[100]; numeric *tmp; int i; if (sprintf(buffer, "%f", d) == 0) return -1; if ((tmp = PGTYPESnumeric_from_asc(buffer, NULL)) == NULL) return -1; i = PGTYPESnumeric_copy(tmp, dst); PGTYPESnumeric_free(tmp); if (i != 0) return -1; errno = 0; return 0;}static intnumericvar_to_double(numeric *var, double *dp){ char *tmp; double val; char *endptr; numeric *varcopy = PGTYPESnumeric_new(); if (PGTYPESnumeric_copy(var, varcopy) < 0) { PGTYPESnumeric_free(varcopy); return -1; } tmp = get_str_from_var(varcopy, varcopy->dscale); PGTYPESnumeric_free(varcopy); if (tmp == NULL) return -1; /* * strtod does not reset errno to 0 in case of success. */ errno = 0; val = strtod(tmp, &endptr); if (errno == ERANGE) { free(tmp); if (val == 0) errno = PGTYPES_NUM_UNDERFLOW; else errno = PGTYPES_NUM_OVERFLOW; return -1; } /* can't free tmp yet, endptr points still into it */ if (*endptr != '\0') { /* shouldn't happen ... */ free(tmp); errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } free(tmp); *dp = val; return 0;}intPGTYPESnumeric_to_double(numeric *nv, double *dp){ double tmp; int i; if ((i = numericvar_to_double(nv, &tmp)) != 0) return -1; *dp = tmp; return 0;}intPGTYPESnumeric_to_int(numeric *nv, int *ip){ long l; int i; if ((i = PGTYPESnumeric_to_long(nv, &l)) != 0) return i; if (l < -INT_MAX || l > INT_MAX) { errno = PGTYPES_NUM_OVERFLOW; return -1; } *ip = (int) l; return 0;}intPGTYPESnumeric_to_long(numeric *nv, long *lp){ char *s = PGTYPESnumeric_to_asc(nv, 0); char *endptr; if (s == NULL) return -1; errno = 0; *lp = strtol(s, &endptr, 10); if (endptr == s) /* this should not happen actually */ return -1; if (errno == ERANGE) { if (*lp == LONG_MIN) errno = PGTYPES_NUM_UNDERFLOW; else errno = PGTYPES_NUM_OVERFLOW; return -1; } free(s); return 0;}intPGTYPESnumeric_to_decimal(numeric *src, decimal *dst){ int i; if (src->ndigits > DECSIZE) { errno = PGTYPES_NUM_OVERFLOW; return -1; } dst->weight = src->weight; dst->rscale = src->rscale; dst->dscale = src->dscale; dst->sign = src->sign; dst->ndigits = src->ndigits; for (i = 0; i < src->ndigits; i++) dst->digits[i] = src->digits[i]; return 0;}intPGTYPESnumeric_from_decimal(decimal *src, numeric *dst){ int i; zero_var(dst); dst->weight = src->weight; dst->rscale = src->rscale; dst->dscale = src->dscale; dst->sign = src->sign; if (alloc_var(dst, src->ndigits) != 0) return -1; for (i = 0; i < src->ndigits; i++) dst->digits[i] = src->digits[i]; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -