#include "postgres_fe.h"
#include <ctype.h>
#include <limits.h>
#include "extern.h"
#include "pgtypes_error.h"
#include "pgtypes_numeric.h"
Go to the source code of this file.
Defines | |
#define | Max(x, y) ((x) > (y) ? (x) : (y)) |
#define | Min(x, y) ((x) < (y) ? (x) : (y)) |
#define | init_var(v) memset(v,0,sizeof(numeric)) |
#define | digitbuf_alloc(size) ((NumericDigit *) pgtypes_alloc(size)) |
#define | digitbuf_free(buf) |
Functions | |
static int | alloc_var (numeric *var, int ndigits) |
numeric * | PGTYPESnumeric_new (void) |
decimal * | PGTYPESdecimal_new (void) |
static int | set_var_from_str (char *str, char **ptr, numeric *dest) |
static char * | get_str_from_var (numeric *var, int dscale) |
numeric * | PGTYPESnumeric_from_asc (char *str, char **endptr) |
char * | PGTYPESnumeric_to_asc (numeric *num, int dscale) |
static void | zero_var (numeric *var) |
void | PGTYPESnumeric_free (numeric *var) |
void | PGTYPESdecimal_free (decimal *var) |
static int | cmp_abs (numeric *var1, numeric *var2) |
static int | add_abs (numeric *var1, numeric *var2, numeric *result) |
static int | sub_abs (numeric *var1, numeric *var2, numeric *result) |
int | PGTYPESnumeric_add (numeric *var1, numeric *var2, numeric *result) |
int | PGTYPESnumeric_sub (numeric *var1, numeric *var2, numeric *result) |
int | PGTYPESnumeric_mul (numeric *var1, numeric *var2, numeric *result) |
static int | select_div_scale (numeric *var1, numeric *var2, int *rscale) |
int | PGTYPESnumeric_div (numeric *var1, numeric *var2, numeric *result) |
int | PGTYPESnumeric_cmp (numeric *var1, numeric *var2) |
int | PGTYPESnumeric_from_int (signed int int_val, numeric *var) |
int | PGTYPESnumeric_from_long (signed long int long_val, numeric *var) |
int | PGTYPESnumeric_copy (numeric *src, numeric *dst) |
int | PGTYPESnumeric_from_double (double d, numeric *dst) |
static int | numericvar_to_double (numeric *var, double *dp) |
int | PGTYPESnumeric_to_double (numeric *nv, double *dp) |
int | PGTYPESnumeric_to_int (numeric *nv, int *ip) |
int | PGTYPESnumeric_to_long (numeric *nv, long *lp) |
int | PGTYPESnumeric_to_decimal (numeric *src, decimal *dst) |
int | PGTYPESnumeric_from_decimal (decimal *src, numeric *dst) |
#define digitbuf_alloc | ( | size | ) | ((NumericDigit *) pgtypes_alloc(size)) |
Definition at line 15 of file numeric.c.
Referenced by add_abs(), alloc_var(), PGTYPESnumeric_div(), PGTYPESnumeric_mul(), and sub_abs().
#define digitbuf_free | ( | buf | ) |
Definition at line 16 of file numeric.c.
Referenced by add_abs(), alloc_var(), PGTYPESnumeric_div(), PGTYPESnumeric_free(), PGTYPESnumeric_mul(), sub_abs(), and zero_var().
#define init_var | ( | v | ) | memset(v,0,sizeof(numeric)) |
Definition at line 13 of file numeric.c.
Referenced by PGTYPESnumeric_div().
#define Max | ( | x, | ||
y | ||||
) | ((x) > (y) ? (x) : (y)) |
Definition at line 10 of file numeric.c.
Referenced by add_abs(), get_str_from_var(), PGTYPESnumeric_add(), PGTYPESnumeric_sub(), select_div_scale(), and sub_abs().
#define Min | ( | x, | ||
y | ||||
) | ((x) < (y) ? (x) : (y)) |
Definition at line 11 of file numeric.c.
Referenced by get_str_from_var(), and select_div_scale().
Definition at line 546 of file numeric.c.
References numeric::buf, digitbuf_alloc, digitbuf_free, numeric::digits, numeric::dscale, i, Max, numeric::ndigits, NULL, numeric::rscale, and numeric::weight.
Referenced by PGTYPESnumeric_add(), and PGTYPESnumeric_sub().
{ NumericDigit *res_buf; NumericDigit *res_digits; int res_ndigits; int res_weight; int res_rscale; int res_dscale; int i, i1, i2; int carry = 0; /* copy these values into local vars for speed in inner loop */ int var1ndigits = var1->ndigits; int var2ndigits = var2->ndigits; NumericDigit *var1digits = var1->digits; NumericDigit *var2digits = var2->digits; res_weight = Max(var1->weight, var2->weight) + 1; res_rscale = Max(var1->rscale, var2->rscale); res_dscale = Max(var1->dscale, var2->dscale); res_ndigits = res_rscale + res_weight + 1; if (res_ndigits <= 0) res_ndigits = 1; if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL) return -1; res_digits = res_buf; i1 = res_rscale + var1->weight + 1; i2 = res_rscale + var2->weight + 1; for (i = res_ndigits - 1; i >= 0; i--) { i1--; i2--; if (i1 >= 0 && i1 < var1ndigits) carry += var1digits[i1]; if (i2 >= 0 && i2 < var2ndigits) carry += var2digits[i2]; if (carry >= 10) { res_digits[i] = carry - 10; carry = 1; } else { res_digits[i] = carry; carry = 0; } } while (res_ndigits > 0 && *res_digits == 0) { res_digits++; res_weight--; res_ndigits--; } while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0) res_ndigits--; if (res_ndigits == 0) res_weight = 0; digitbuf_free(result->buf); result->ndigits = res_ndigits; result->buf = res_buf; result->digits = res_digits; result->weight = res_weight; result->rscale = res_rscale; result->dscale = res_dscale; return 0; }
static int alloc_var | ( | numeric * | var, | |
int | ndigits | |||
) | [static] |
Definition at line 113 of file numeric.c.
References numeric::buf, digitbuf_alloc, digitbuf_free, numeric::digits, numeric::ndigits, and NULL.
Referenced by PGTYPESnumeric_copy(), PGTYPESnumeric_from_decimal(), PGTYPESnumeric_from_long(), PGTYPESnumeric_new(), and set_var_from_str().
{ digitbuf_free(var->buf); var->buf = digitbuf_alloc(ndigits + 1); if (var->buf == NULL) return -1; var->buf[0] = 0; var->digits = var->buf + 1; var->ndigits = ndigits; return 0; }
Definition at line 488 of file numeric.c.
References numeric::digits, and numeric::weight.
Referenced by PGTYPESnumeric_add(), PGTYPESnumeric_cmp(), PGTYPESnumeric_div(), and PGTYPESnumeric_sub().
{ int i1 = 0; int i2 = 0; int w1 = var1->weight; int w2 = var2->weight; int stat; while (w1 > w2 && i1 < var1->ndigits) { if (var1->digits[i1++] != 0) return 1; w1--; } while (w2 > w1 && i2 < var2->ndigits) { if (var2->digits[i2++] != 0) return -1; w2--; } if (w1 == w2) { while (i1 < var1->ndigits && i2 < var2->ndigits) { stat = var1->digits[i1++] - var2->digits[i2++]; if (stat) { if (stat > 0) return 1; return -1; } } } while (i1 < var1->ndigits) { if (var1->digits[i1++] != 0) return 1; } while (i2 < var2->ndigits) { if (var2->digits[i2++] != 0) return -1; } return 0; }
static char* get_str_from_var | ( | numeric * | var, | |
int | dscale | |||
) | [static] |
Definition at line 311 of file numeric.c.
References numeric::digits, i, Max, Min, numeric::ndigits, NULL, NUMERIC_NAN, NUMERIC_NEG, pgtypes_alloc(), numeric::sign, and numeric::weight.
Referenced by numericvar_to_double(), and PGTYPESnumeric_to_asc().
{ char *str; char *cp; int i; int d; if (var->sign == NUMERIC_NAN) { str = (char *) pgtypes_alloc(4); if (str == NULL) return NULL; sprintf(str, "NaN"); return str; } /* * Check if we must round up before printing the value and do so. */ i = dscale + var->weight + 1; if (i >= 0 && var->ndigits > i) { int carry = (var->digits[i] > 4) ? 1 : 0; var->ndigits = i; while (carry) { carry += var->digits[--i]; var->digits[i] = carry % 10; carry /= 10; } if (i < 0) { var->digits--; var->ndigits++; var->weight++; } } else var->ndigits = Max(0, Min(i, var->ndigits)); /* * Allocate space for the result */ if ((str = (char *) pgtypes_alloc(Max(0, dscale) + Max(0, var->weight) + 4)) == NULL) return NULL; cp = str; /* * Output a dash for negative values */ if (var->sign == NUMERIC_NEG) *cp++ = '-'; /* * Output all digits before the decimal point */ i = Max(var->weight, 0); d = 0; while (i >= 0) { if (i <= var->weight && d < var->ndigits) *cp++ = var->digits[d++] + '0'; else *cp++ = '0'; i--; } /* * If requested, output a decimal point and all the digits that follow it. */ if (dscale > 0) { *cp++ = '.'; while (i >= -dscale) { if (i <= var->weight && d < var->ndigits) *cp++ = var->digits[d++] + '0'; else *cp++ = '0'; i--; } } /* * terminate the string and return it */ *cp = '\0'; return str; }
static int numericvar_to_double | ( | numeric * | var, | |
double * | dp | |||
) | [static] |
Definition at line 1516 of file numeric.c.
References numeric::dscale, free, get_str_from_var(), NULL, PGTYPESnumeric_copy(), PGTYPESnumeric_free(), PGTYPESnumeric_new(), and val.
Referenced by PGTYPESnumeric_to_double().
{ 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; }
void PGTYPESdecimal_free | ( | decimal * | var | ) |
decimal* PGTYPESdecimal_new | ( | void | ) |
Definition at line 718 of file numeric.c.
References add_abs(), cmp_abs(), numeric::dscale, Max, NUMERIC_POS, numeric::rscale, numeric::sign, sub_abs(), and zero_var().
Referenced by decadd(), and main().
{ /* * Decide on the signs of the two variables what to do */ if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_POS) { /* * Both are positive result = +(ABS(var1) + ABS(var2)) */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; } else { /* * var1 is positive, var2 is negative Must compare absolute values */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = +(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = -(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_NEG; break; } } } else { if (var2->sign == NUMERIC_POS) { /* ---------- * var1 is negative, var2 is positive * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = -(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = +(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_POS; break; } } else { /* ---------- * Both are negative * result = -(ABS(var1) + ABS(var2)) * ---------- */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; } } return 0; }
Definition at line 1363 of file numeric.c.
References cmp_abs(), NUMERIC_NEG, NUMERIC_POS, and numeric::sign.
Referenced by deccmp(), and main().
{ /* 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; }
Definition at line 1472 of file numeric.c.
References alloc_var(), numeric::digits, numeric::dscale, i, numeric::ndigits, NULL, numeric::rscale, numeric::sign, numeric::weight, and zero_var().
Referenced by ecpg_get_data(), ecpg_store_input(), main(), numericvar_to_double(), PGTYPESnumeric_from_double(), and PGTYPESnumeric_to_asc().
Definition at line 1134 of file numeric.c.
References buf, numeric::buf, cmp_abs(), digitbuf_alloc, digitbuf_free, digits, numeric::digits, numeric::dscale, i, init_var, numeric::ndigits, NULL, numeric::rscale, select_div_scale(), numeric::sign, sub_abs(), numeric::weight, and zero_var().
Referenced by decdiv(), and main().
{ 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; }
void PGTYPESnumeric_free | ( | numeric * | var | ) |
Definition at line 466 of file numeric.c.
References numeric::buf, digitbuf_free, and free.
Referenced by deccall2(), deccall3(), deccvasc(), deccvdbl(), deccvint(), deccvlong(), dectoasc(), dectodbl(), dectoint(), dectolong(), ecpg_get_data(), ecpg_set_compat_sqlda(), ecpg_set_native_sqlda(), ecpg_store_input(), main(), numericvar_to_double(), PGTYPESnumeric_from_asc(), PGTYPESnumeric_from_double(), PGTYPESnumeric_to_asc(), and sqlda_common_total_size().
{ digitbuf_free(var->buf); free(var); }
numeric* PGTYPESnumeric_from_asc | ( | char * | str, | |
char ** | endptr | |||
) |
Definition at line 406 of file numeric.c.
References NULL, pgtypes_alloc(), PGTYPESnumeric_free(), set_var_from_str(), and value.
Referenced by deccvasc(), ecpg_get_data(), ecpg_set_compat_sqlda(), ecpg_set_native_sqlda(), main(), PGTYPESnumeric_from_double(), and sqlda_common_total_size().
{ numeric *value = (numeric *) pgtypes_alloc(sizeof(numeric)); int ret; char *realptr; char **ptr = (endptr != NULL) ? endptr : &realptr; if (!value) return (NULL); ret = set_var_from_str(str, ptr, value); if (ret) { PGTYPESnumeric_free(value); return (NULL); } return (value); }
Definition at line 1646 of file numeric.c.
References alloc_var(), decimal::digits, numeric::digits, decimal::dscale, numeric::dscale, i, decimal::ndigits, decimal::rscale, numeric::rscale, decimal::sign, numeric::sign, decimal::weight, numeric::weight, and zero_var().
Referenced by deccall2(), deccall3(), dectoasc(), dectodbl(), dectoint(), dectolong(), ecpg_store_input(), and main().
int PGTYPESnumeric_from_double | ( | double | d, | |
numeric * | dst | |||
) |
Definition at line 1495 of file numeric.c.
References i, NULL, PGTYPESnumeric_copy(), PGTYPESnumeric_free(), and PGTYPESnumeric_from_asc().
Referenced by deccvdbl().
{ 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; }
int PGTYPESnumeric_from_int | ( | signed int | int_val, | |
numeric * | var | |||
) |
Definition at line 1393 of file numeric.c.
References PGTYPESnumeric_from_long().
Referenced by deccvint(), and main().
{ /* implicit conversion */ signed long int long_int = int_val; return PGTYPESnumeric_from_long(long_int, var); }
int PGTYPESnumeric_from_long | ( | signed long int | long_val, | |
numeric * | var | |||
) |
Definition at line 1402 of file numeric.c.
References alloc_var(), numeric::digits, numeric::dscale, i, numeric::rscale, numeric::sign, and numeric::weight.
Referenced by deccvlong(), main(), and PGTYPESnumeric_from_int().
{ /* 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; }
Definition at line 977 of file numeric.c.
References numeric::buf, digitbuf_alloc, digitbuf_free, numeric::digits, numeric::dscale, i, numeric::ndigits, NULL, numeric::rscale, numeric::sign, and numeric::weight.
Referenced by decmul(), and main().
{ NumericDigit *res_buf; NumericDigit *res_digits; int res_ndigits; int res_weight; int res_sign; int i, ri, i1, i2; long sum = 0; int global_rscale = var1->rscale + var2->rscale; res_weight = var1->weight + var2->weight + 2; res_ndigits = var1->ndigits + var2->ndigits + 1; if (var1->sign == var2->sign) res_sign = NUMERIC_POS; else res_sign = NUMERIC_NEG; if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL) return -1; res_digits = res_buf; memset(res_digits, 0, res_ndigits); ri = res_ndigits; for (i1 = var1->ndigits - 1; i1 >= 0; i1--) { sum = 0; i = --ri; for (i2 = var2->ndigits - 1; i2 >= 0; i2--) { sum += res_digits[i] + var1->digits[i1] * var2->digits[i2]; res_digits[i--] = sum % 10; sum /= 10; } res_digits[i] = sum; } i = res_weight + global_rscale + 2; if (i >= 0 && i < res_ndigits) { sum = (res_digits[i] > 4) ? 1 : 0; res_ndigits = i; i--; while (sum) { sum += res_digits[i]; res_digits[i--] = sum % 10; sum /= 10; } } while (res_ndigits > 0 && *res_digits == 0) { res_digits++; res_weight--; res_ndigits--; } while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0) res_ndigits--; if (res_ndigits == 0) { res_sign = NUMERIC_POS; res_weight = 0; } digitbuf_free(result->buf); result->buf = res_buf; result->digits = res_digits; result->ndigits = res_ndigits; result->weight = res_weight; result->rscale = global_rscale; result->sign = res_sign; result->dscale = var1->dscale + var2->dscale; return 0; }
numeric* PGTYPESnumeric_new | ( | void | ) |
Definition at line 126 of file numeric.c.
References alloc_var(), free, NULL, and pgtypes_alloc().
Referenced by deccall2(), deccall3(), deccvdbl(), deccvint(), deccvlong(), dectoasc(), dectodbl(), dectoint(), dectolong(), ecpg_get_data(), ecpg_store_input(), main(), numericvar_to_double(), and PGTYPESnumeric_to_asc().
Definition at line 846 of file numeric.c.
References add_abs(), cmp_abs(), numeric::dscale, Max, NUMERIC_NEG, NUMERIC_POS, numeric::rscale, numeric::sign, sub_abs(), and zero_var().
Referenced by decsub(), and main().
{ /* * Decide on the signs of the two variables what to do */ if (var1->sign == NUMERIC_POS) { if (var2->sign == NUMERIC_NEG) { /* ---------- * var1 is positive, var2 is negative * result = +(ABS(var1) + ABS(var2)) * ---------- */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; } else { /* ---------- * Both are positive * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = +(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_POS; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = -(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_NEG; break; } } } else { if (var2->sign == NUMERIC_NEG) { /* ---------- * Both are negative * Must compare absolute values * ---------- */ switch (cmp_abs(var1, var2)) { case 0: /* ---------- * ABS(var1) == ABS(var2) * result = ZERO * ---------- */ zero_var(result); result->rscale = Max(var1->rscale, var2->rscale); result->dscale = Max(var1->dscale, var2->dscale); break; case 1: /* ---------- * ABS(var1) > ABS(var2) * result = -(ABS(var1) - ABS(var2)) * ---------- */ if (sub_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; break; case -1: /* ---------- * ABS(var1) < ABS(var2) * result = +(ABS(var2) - ABS(var1)) * ---------- */ if (sub_abs(var2, var1, result) != 0) return -1; result->sign = NUMERIC_POS; break; } } else { /* ---------- * var1 is negative, var2 is positive * result = -(ABS(var1) + ABS(var2)) * ---------- */ if (add_abs(var1, var2, result) != 0) return -1; result->sign = NUMERIC_NEG; } } return 0; }
char* PGTYPESnumeric_to_asc | ( | numeric * | num, | |
int | dscale | |||
) |
Definition at line 428 of file numeric.c.
References numeric::dscale, get_str_from_var(), PGTYPESnumeric_copy(), PGTYPESnumeric_free(), and PGTYPESnumeric_new().
Referenced by dectoasc(), dump_sqlda(), ecpg_store_input(), main(), and PGTYPESnumeric_to_long().
{ numeric *numcopy = PGTYPESnumeric_new(); char *s; if (dscale < 0) dscale = num->dscale; if (PGTYPESnumeric_copy(num, numcopy) < 0) { PGTYPESnumeric_free(numcopy); return NULL; } /* get_str_from_var may change its argument */ s = get_str_from_var(numcopy, dscale); PGTYPESnumeric_free(numcopy); return (s); }
Definition at line 1623 of file numeric.c.
References DECSIZE, numeric::digits, decimal::digits, numeric::dscale, decimal::dscale, i, decimal::ndigits, numeric::ndigits, numeric::rscale, decimal::rscale, numeric::sign, decimal::sign, numeric::weight, and decimal::weight.
Referenced by deccall3(), deccvasc(), deccvdbl(), deccvint(), deccvlong(), ecpg_get_data(), and main().
int PGTYPESnumeric_to_double | ( | numeric * | nv, | |
double * | dp | |||
) |
Definition at line 1564 of file numeric.c.
References numericvar_to_double().
Referenced by dectodbl(), and main().
{ double tmp; if (numericvar_to_double(nv, &tmp) != 0) return -1; *dp = tmp; return 0; }
int PGTYPESnumeric_to_int | ( | numeric * | nv, | |
int * | ip | |||
) |
Definition at line 1575 of file numeric.c.
References i, and PGTYPESnumeric_to_long().
Referenced by dectoint(), and main().
{ 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; }
int PGTYPESnumeric_to_long | ( | numeric * | nv, | |
long * | lp | |||
) |
Definition at line 1594 of file numeric.c.
References free, NULL, and PGTYPESnumeric_to_asc().
Referenced by dectolong(), main(), and PGTYPESnumeric_to_int().
{ 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 */ free(s); return -1; } free(s); if (errno == ERANGE) { if (*lp == LONG_MIN) errno = PGTYPES_NUM_UNDERFLOW; else errno = PGTYPES_NUM_OVERFLOW; return -1; } return 0; }
Definition at line 1068 of file numeric.c.
References numeric::digits, numeric::dscale, i, Max, Min, numeric::ndigits, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, and numeric::weight.
Referenced by PGTYPESnumeric_div().
{ int weight1, weight2, qweight, i; NumericDigit firstdigit1, firstdigit2; int res_dscale; /* * The result scale of a division isn't specified in any SQL standard. For * PostgreSQL we select a display scale that will give at least * NUMERIC_MIN_SIG_DIGITS significant digits, so that numeric gives a * result no less accurate than float8; but use a scale not less than * either input's display scale. */ /* Get the actual (normalized) weight and first digit of each input */ weight1 = 0; /* values to use if var1 is zero */ firstdigit1 = 0; for (i = 0; i < var1->ndigits; i++) { firstdigit1 = var1->digits[i]; if (firstdigit1 != 0) { weight1 = var1->weight - i; break; } } weight2 = 0; /* values to use if var2 is zero */ firstdigit2 = 0; for (i = 0; i < var2->ndigits; i++) { firstdigit2 = var2->digits[i]; if (firstdigit2 != 0) { weight2 = var2->weight - i; break; } } /* * Estimate weight of quotient. If the two first digits are equal, we * can't be sure, but assume that var1 is less than var2. */ qweight = weight1 - weight2; if (firstdigit1 <= firstdigit2) qweight--; /* 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_dscale + 4; return res_dscale; }
static int set_var_from_str | ( | char * | str, | |
char ** | ptr, | |||
numeric * | dest | |||
) | [static] |
Definition at line 162 of file numeric.c.
References alloc_var(), numeric::digits, numeric::dscale, i, numeric::ndigits, NUMERIC_MAX_PRECISION, pg_strncasecmp(), numeric::rscale, numeric::sign, and numeric::weight.
Referenced by PGTYPESnumeric_from_asc().
{ bool have_dp = FALSE; int i = 0; errno = 0; *ptr = str; while (*(*ptr)) { if (!isspace((unsigned char) *(*ptr))) break; (*ptr)++; } if (pg_strncasecmp(*ptr, "NaN", 3) == 0) { *ptr += 3; dest->sign = NUMERIC_NAN; /* Should be nothing left but spaces */ while (*(*ptr)) { if (!isspace((unsigned char) *(*ptr))) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } (*ptr)++; } return 0; } if (alloc_var(dest, strlen((*ptr))) < 0) return -1; dest->weight = -1; dest->dscale = 0; dest->sign = NUMERIC_POS; switch (*(*ptr)) { case '+': dest->sign = NUMERIC_POS; (*ptr)++; break; case '-': dest->sign = NUMERIC_NEG; (*ptr)++; break; } if (*(*ptr) == '.') { have_dp = TRUE; (*ptr)++; } if (!isdigit((unsigned char) *(*ptr))) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } while (*(*ptr)) { if (isdigit((unsigned char) *(*ptr))) { dest->digits[i++] = *(*ptr)++ - '0'; if (!have_dp) dest->weight++; else dest->dscale++; } else if (*(*ptr) == '.') { if (have_dp) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } have_dp = TRUE; (*ptr)++; } else break; } dest->ndigits = i; /* Handle exponent, if any */ if (*(*ptr) == 'e' || *(*ptr) == 'E') { long exponent; char *endptr; (*ptr)++; exponent = strtol(*ptr, &endptr, 10); if (endptr == (*ptr)) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } (*ptr) = endptr; if (exponent > NUMERIC_MAX_PRECISION || exponent < -NUMERIC_MAX_PRECISION) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } dest->weight += (int) exponent; dest->dscale -= (int) exponent; if (dest->dscale < 0) dest->dscale = 0; } /* Should be nothing left but spaces */ while (*(*ptr)) { if (!isspace((unsigned char) *(*ptr))) { errno = PGTYPES_NUM_BAD_NUMERIC; return -1; } (*ptr)++; } /* Strip any leading zeroes */ while (dest->ndigits > 0 && *(dest->digits) == 0) { (dest->digits)++; (dest->weight)--; (dest->ndigits)--; } if (dest->ndigits == 0) dest->weight = 0; dest->rscale = dest->dscale; return (0); }
Definition at line 634 of file numeric.c.
References numeric::buf, digitbuf_alloc, digitbuf_free, numeric::digits, numeric::dscale, i, Max, numeric::ndigits, NULL, numeric::rscale, and numeric::weight.
Referenced by PGTYPESnumeric_add(), PGTYPESnumeric_div(), and PGTYPESnumeric_sub().
{ NumericDigit *res_buf; NumericDigit *res_digits; int res_ndigits; int res_weight; int res_rscale; int res_dscale; int i, i1, i2; int borrow = 0; /* copy these values into local vars for speed in inner loop */ int var1ndigits = var1->ndigits; int var2ndigits = var2->ndigits; NumericDigit *var1digits = var1->digits; NumericDigit *var2digits = var2->digits; res_weight = var1->weight; res_rscale = Max(var1->rscale, var2->rscale); res_dscale = Max(var1->dscale, var2->dscale); res_ndigits = res_rscale + res_weight + 1; if (res_ndigits <= 0) res_ndigits = 1; if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL) return -1; res_digits = res_buf; i1 = res_rscale + var1->weight + 1; i2 = res_rscale + var2->weight + 1; for (i = res_ndigits - 1; i >= 0; i--) { i1--; i2--; if (i1 >= 0 && i1 < var1ndigits) borrow += var1digits[i1]; if (i2 >= 0 && i2 < var2ndigits) borrow -= var2digits[i2]; if (borrow < 0) { res_digits[i] = borrow + 10; borrow = -1; } else { res_digits[i] = borrow; borrow = 0; } } while (res_ndigits > 0 && *res_digits == 0) { res_digits++; res_weight--; res_ndigits--; } while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0) res_ndigits--; if (res_ndigits == 0) res_weight = 0; digitbuf_free(result->buf); result->ndigits = res_ndigits; result->buf = res_buf; result->digits = res_digits; result->weight = res_weight; result->rscale = res_rscale; result->dscale = res_dscale; return 0; }
static void zero_var | ( | numeric * | var | ) | [static] |
Definition at line 455 of file numeric.c.
References numeric::buf, digitbuf_free, numeric::digits, numeric::ndigits, numeric::sign, and numeric::weight.
Referenced by PGTYPESnumeric_add(), PGTYPESnumeric_copy(), PGTYPESnumeric_div(), PGTYPESnumeric_from_decimal(), and PGTYPESnumeric_sub().