Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

numeric.c File Reference

#include "postgres.h"
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include "access/hash.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/int8.h"
#include "utils/numeric.h"
Include dependency graph for numeric.c:

Go to the source code of this file.

Data Structures

struct  NumericShort
struct  NumericLong
union  NumericChoice
struct  NumericData
struct  NumericVar
struct  Int8TransTypeData

Defines

#define NBASE   10000
#define HALF_NBASE   5000
#define DEC_DIGITS   4
#define MUL_GUARD_DIGITS   2
#define DIV_GUARD_DIGITS   4
#define NUMERIC_SIGN_MASK   0xC000
#define NUMERIC_POS   0x0000
#define NUMERIC_NEG   0x4000
#define NUMERIC_SHORT   0x8000
#define NUMERIC_NAN   0xC000
#define NUMERIC_FLAGBITS(n)   ((n)->choice.n_header & NUMERIC_SIGN_MASK)
#define NUMERIC_IS_NAN(n)   (NUMERIC_FLAGBITS(n) == NUMERIC_NAN)
#define NUMERIC_IS_SHORT(n)   (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT)
#define NUMERIC_HDRSZ   (VARHDRSZ + sizeof(uint16) + sizeof(int16))
#define NUMERIC_HDRSZ_SHORT   (VARHDRSZ + sizeof(uint16))
#define NUMERIC_HEADER_SIZE(n)
#define NUMERIC_SHORT_SIGN_MASK   0x2000
#define NUMERIC_SHORT_DSCALE_MASK   0x1F80
#define NUMERIC_SHORT_DSCALE_SHIFT   7
#define NUMERIC_SHORT_DSCALE_MAX   (NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT)
#define NUMERIC_SHORT_WEIGHT_SIGN_MASK   0x0040
#define NUMERIC_SHORT_WEIGHT_MASK   0x003F
#define NUMERIC_SHORT_WEIGHT_MAX   NUMERIC_SHORT_WEIGHT_MASK
#define NUMERIC_SHORT_WEIGHT_MIN   (-(NUMERIC_SHORT_WEIGHT_MASK+1))
#define NUMERIC_DSCALE_MASK   0x3FFF
#define NUMERIC_SIGN(n)
#define NUMERIC_DSCALE(n)
#define NUMERIC_WEIGHT(n)
#define dump_numeric(s, n)
#define dump_var(s, v)
#define digitbuf_alloc(ndigits)   ((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit)))
#define digitbuf_free(buf)
#define init_var(v)   MemSetAligned(v, 0, sizeof(NumericVar))
#define NUMERIC_DIGITS(num)
#define NUMERIC_NDIGITS(num)   ((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit))
#define NUMERIC_CAN_BE_SHORT(scale, weight)

Typedefs

typedef int16 NumericDigit
typedef struct NumericVar NumericVar
typedef struct Int8TransTypeData Int8TransTypeData

Functions

static void alloc_var (NumericVar *var, int ndigits)
static void free_var (NumericVar *var)
static void zero_var (NumericVar *var)
static const char * set_var_from_str (const char *str, const char *cp, NumericVar *dest)
static void set_var_from_num (Numeric value, NumericVar *dest)
static void init_var_from_num (Numeric num, NumericVar *dest)
static void set_var_from_var (NumericVar *value, NumericVar *dest)
static char * get_str_from_var (NumericVar *var)
static char * get_str_from_var_sci (NumericVar *var, int rscale)
static Numeric make_result (NumericVar *var)
static void apply_typmod (NumericVar *var, int32 typmod)
static int32 numericvar_to_int4 (NumericVar *var)
static bool numericvar_to_int8 (NumericVar *var, int64 *result)
static void int8_to_numericvar (int64 val, NumericVar *var)
static double numeric_to_double_no_overflow (Numeric num)
static double numericvar_to_double_no_overflow (NumericVar *var)
static int cmp_numerics (Numeric num1, Numeric num2)
static int cmp_var (NumericVar *var1, NumericVar *var2)
static int cmp_var_common (const NumericDigit *var1digits, int var1ndigits, int var1weight, int var1sign, const NumericDigit *var2digits, int var2ndigits, int var2weight, int var2sign)
static void add_var (NumericVar *var1, NumericVar *var2, NumericVar *result)
static void sub_var (NumericVar *var1, NumericVar *var2, NumericVar *result)
static void mul_var (NumericVar *var1, NumericVar *var2, NumericVar *result, int rscale)
static void div_var (NumericVar *var1, NumericVar *var2, NumericVar *result, int rscale, bool round)
static void div_var_fast (NumericVar *var1, NumericVar *var2, NumericVar *result, int rscale, bool round)
static int select_div_scale (NumericVar *var1, NumericVar *var2)
static void mod_var (NumericVar *var1, NumericVar *var2, NumericVar *result)
static void ceil_var (NumericVar *var, NumericVar *result)
static void floor_var (NumericVar *var, NumericVar *result)
static void sqrt_var (NumericVar *arg, NumericVar *result, int rscale)
static void exp_var (NumericVar *arg, NumericVar *result, int rscale)
static void exp_var_internal (NumericVar *arg, NumericVar *result, int rscale)
static void ln_var (NumericVar *arg, NumericVar *result, int rscale)
static void log_var (NumericVar *base, NumericVar *num, NumericVar *result)
static void power_var (NumericVar *base, NumericVar *exp, NumericVar *result)
static void power_var_int (NumericVar *base, int exp, NumericVar *result, int rscale)
static int cmp_abs (NumericVar *var1, NumericVar *var2)
static int cmp_abs_common (const NumericDigit *var1digits, int var1ndigits, int var1weight, const NumericDigit *var2digits, int var2ndigits, int var2weight)
static void add_abs (NumericVar *var1, NumericVar *var2, NumericVar *result)
static void sub_abs (NumericVar *var1, NumericVar *var2, NumericVar *result)
static void round_var (NumericVar *var, int rscale)
static void trunc_var (NumericVar *var, int rscale)
static void strip_var (NumericVar *var)
static void compute_bucket (Numeric operand, Numeric bound1, Numeric bound2, NumericVar *count_var, NumericVar *result_var)
Datum numeric_in (PG_FUNCTION_ARGS)
Datum numeric_out (PG_FUNCTION_ARGS)
bool numeric_is_nan (Numeric num)
int32 numeric_maximum_size (int32 typmod)
char * numeric_out_sci (Numeric num, int scale)
Datum numeric_recv (PG_FUNCTION_ARGS)
Datum numeric_send (PG_FUNCTION_ARGS)
Datum numeric_transform (PG_FUNCTION_ARGS)
Datum numeric (PG_FUNCTION_ARGS)
Datum numerictypmodin (PG_FUNCTION_ARGS)
Datum numerictypmodout (PG_FUNCTION_ARGS)
Datum numeric_abs (PG_FUNCTION_ARGS)
Datum numeric_uminus (PG_FUNCTION_ARGS)
Datum numeric_uplus (PG_FUNCTION_ARGS)
Datum numeric_sign (PG_FUNCTION_ARGS)
Datum numeric_round (PG_FUNCTION_ARGS)
Datum numeric_trunc (PG_FUNCTION_ARGS)
Datum numeric_ceil (PG_FUNCTION_ARGS)
Datum numeric_floor (PG_FUNCTION_ARGS)
Datum width_bucket_numeric (PG_FUNCTION_ARGS)
Datum numeric_cmp (PG_FUNCTION_ARGS)
Datum numeric_eq (PG_FUNCTION_ARGS)
Datum numeric_ne (PG_FUNCTION_ARGS)
Datum numeric_gt (PG_FUNCTION_ARGS)
Datum numeric_ge (PG_FUNCTION_ARGS)
Datum numeric_lt (PG_FUNCTION_ARGS)
Datum numeric_le (PG_FUNCTION_ARGS)
Datum hash_numeric (PG_FUNCTION_ARGS)
Datum numeric_add (PG_FUNCTION_ARGS)
Datum numeric_sub (PG_FUNCTION_ARGS)
Datum numeric_mul (PG_FUNCTION_ARGS)
Datum numeric_div (PG_FUNCTION_ARGS)
Datum numeric_div_trunc (PG_FUNCTION_ARGS)
Datum numeric_mod (PG_FUNCTION_ARGS)
Datum numeric_inc (PG_FUNCTION_ARGS)
Datum numeric_smaller (PG_FUNCTION_ARGS)
Datum numeric_larger (PG_FUNCTION_ARGS)
Datum numeric_fac (PG_FUNCTION_ARGS)
Datum numeric_sqrt (PG_FUNCTION_ARGS)
Datum numeric_exp (PG_FUNCTION_ARGS)
Datum numeric_ln (PG_FUNCTION_ARGS)
Datum numeric_log (PG_FUNCTION_ARGS)
Datum numeric_power (PG_FUNCTION_ARGS)
Datum int4_numeric (PG_FUNCTION_ARGS)
Datum numeric_int4 (PG_FUNCTION_ARGS)
Datum int8_numeric (PG_FUNCTION_ARGS)
Datum numeric_int8 (PG_FUNCTION_ARGS)
Datum int2_numeric (PG_FUNCTION_ARGS)
Datum numeric_int2 (PG_FUNCTION_ARGS)
Datum float8_numeric (PG_FUNCTION_ARGS)
Datum numeric_float8 (PG_FUNCTION_ARGS)
Datum numeric_float8_no_overflow (PG_FUNCTION_ARGS)
Datum float4_numeric (PG_FUNCTION_ARGS)
Datum numeric_float4 (PG_FUNCTION_ARGS)
static ArrayTypedo_numeric_accum (ArrayType *transarray, Numeric newval)
static ArrayTypedo_numeric_avg_accum (ArrayType *transarray, Numeric newval)
Datum numeric_accum (PG_FUNCTION_ARGS)
Datum numeric_avg_accum (PG_FUNCTION_ARGS)
Datum int2_accum (PG_FUNCTION_ARGS)
Datum int4_accum (PG_FUNCTION_ARGS)
Datum int8_accum (PG_FUNCTION_ARGS)
Datum int8_avg_accum (PG_FUNCTION_ARGS)
Datum numeric_avg (PG_FUNCTION_ARGS)
static Numeric numeric_stddev_internal (ArrayType *transarray, bool variance, bool sample, bool *is_null)
Datum numeric_var_samp (PG_FUNCTION_ARGS)
Datum numeric_stddev_samp (PG_FUNCTION_ARGS)
Datum numeric_var_pop (PG_FUNCTION_ARGS)
Datum numeric_stddev_pop (PG_FUNCTION_ARGS)
Datum int2_sum (PG_FUNCTION_ARGS)
Datum int4_sum (PG_FUNCTION_ARGS)
Datum int8_sum (PG_FUNCTION_ARGS)
Datum int2_avg_accum (PG_FUNCTION_ARGS)
Datum int4_avg_accum (PG_FUNCTION_ARGS)
Datum int8_avg (PG_FUNCTION_ARGS)

Variables

static NumericDigit const_zero_data [1] = {0}
static NumericVar const_zero
static NumericDigit const_one_data [1] = {1}
static NumericVar const_one
static NumericDigit const_two_data [1] = {2}
static NumericVar const_two
static NumericDigit const_ten_data [1] = {10}
static NumericVar const_ten
static NumericDigit const_zero_point_five_data [1] = {5000}
static NumericVar const_zero_point_five
static NumericDigit const_zero_point_nine_data [1] = {9000}
static NumericVar const_zero_point_nine
static NumericDigit const_zero_point_01_data [1] = {100}
static NumericVar const_zero_point_01
static NumericDigit const_one_point_one_data [2] = {1, 1000}
static NumericVar const_one_point_one
static NumericVar const_nan
static const int round_powers [4] = {0, 1000, 100, 10}

Define Documentation

#define DEC_DIGITS   4
#define digitbuf_alloc (   ndigits  )     ((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit)))

Definition at line 354 of file numeric.c.

Referenced by add_abs(), alloc_var(), set_var_from_var(), and sub_abs().

#define digitbuf_free (   buf  ) 
Value:
do { \
         if ((buf) != NULL) \
             pfree(buf); \
    } while (0)

Definition at line 356 of file numeric.c.

Referenced by add_abs(), alloc_var(), free_var(), set_var_from_var(), sub_abs(), and zero_var().

#define DIV_GUARD_DIGITS   4

Definition at line 87 of file numeric.c.

Referenced by div_var_fast().

#define dump_numeric (   s,
  n 
)

Definition at line 350 of file numeric.c.

Referenced by make_result().

#define dump_var (   s,
  v 
)

Definition at line 351 of file numeric.c.

#define HALF_NBASE   5000

Definition at line 84 of file numeric.c.

Referenced by div_var().

#define init_var (   v  )     MemSetAligned(v, 0, sizeof(NumericVar))
#define MUL_GUARD_DIGITS   2

Definition at line 86 of file numeric.c.

Referenced by exp_var(), mul_var(), and power_var_int().

#define NBASE   10000
#define NUMERIC_CAN_BE_SHORT (   scale,
  weight 
)
Value:

Definition at line 368 of file numeric.c.

Referenced by make_result(), and numeric().

#define NUMERIC_DIGITS (   num  ) 
Value:
(NUMERIC_IS_SHORT(num) ? \
    (num)->choice.n_short.n_data : (num)->choice.n_long.n_data)

Definition at line 364 of file numeric.c.

Referenced by cmp_numerics(), hash_numeric(), init_var_from_num(), make_result(), and set_var_from_num().

#define NUMERIC_DSCALE (   n  ) 
Value:
(NUMERIC_IS_SHORT((n)) ? \
    ((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \
        >> NUMERIC_SHORT_DSCALE_SHIFT \
    : ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK))

Definition at line 199 of file numeric.c.

Referenced by init_var_from_num(), make_result(), numeric(), numeric_abs(), numeric_uminus(), and set_var_from_num().

#define NUMERIC_DSCALE_MASK   0x3FFF

Definition at line 193 of file numeric.c.

Referenced by numeric().

#define NUMERIC_FLAGBITS (   n  )     ((n)->choice.n_header & NUMERIC_SIGN_MASK)

Definition at line 159 of file numeric.c.

#define NUMERIC_HDRSZ   (VARHDRSZ + sizeof(uint16) + sizeof(int16))

Definition at line 163 of file numeric.c.

Referenced by make_result(), and numeric_maximum_size().

#define NUMERIC_HDRSZ_SHORT   (VARHDRSZ + sizeof(uint16))

Definition at line 164 of file numeric.c.

Referenced by make_result().

#define NUMERIC_HEADER_SIZE (   n  ) 
Value:
(VARHDRSZ + sizeof(uint16) + \
        (((NUMERIC_FLAGBITS(n) & 0x8000) == 0) ? sizeof(int16) : 0))

Definition at line 171 of file numeric.c.

#define NUMERIC_IS_NAN (   n  )     (NUMERIC_FLAGBITS(n) == NUMERIC_NAN)
#define NUMERIC_IS_SHORT (   n  )     (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT)

Definition at line 161 of file numeric.c.

Referenced by numeric(), numeric_abs(), and numeric_uminus().

#define NUMERIC_NAN   0xC000

Definition at line 157 of file numeric.c.

Referenced by get_str_from_var(), main(), make_result(), and numeric_recv().

#define NUMERIC_NDIGITS (   num  )     ((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit))
#define NUMERIC_NEG   0x4000
#define NUMERIC_POS   0x0000
#define NUMERIC_SHORT   0x8000

Definition at line 156 of file numeric.c.

Referenced by make_result().

#define NUMERIC_SHORT_DSCALE_MASK   0x1F80

Definition at line 180 of file numeric.c.

#define NUMERIC_SHORT_DSCALE_MAX   (NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT)

Definition at line 182 of file numeric.c.

#define NUMERIC_SHORT_DSCALE_SHIFT   7

Definition at line 181 of file numeric.c.

Referenced by numeric().

#define NUMERIC_SHORT_SIGN_MASK   0x2000

Definition at line 179 of file numeric.c.

#define NUMERIC_SHORT_WEIGHT_MASK   0x003F

Definition at line 185 of file numeric.c.

#define NUMERIC_SHORT_WEIGHT_MAX   NUMERIC_SHORT_WEIGHT_MASK

Definition at line 186 of file numeric.c.

#define NUMERIC_SHORT_WEIGHT_MIN   (-(NUMERIC_SHORT_WEIGHT_MASK+1))

Definition at line 187 of file numeric.c.

#define NUMERIC_SHORT_WEIGHT_SIGN_MASK   0x0040

Definition at line 184 of file numeric.c.

Referenced by make_result().

#define NUMERIC_SIGN (   n  ) 
Value:
(NUMERIC_IS_SHORT(n) ? \
        (((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \
        NUMERIC_NEG : NUMERIC_POS) : NUMERIC_FLAGBITS(n))

Definition at line 195 of file numeric.c.

Referenced by cmp_numerics(), init_var_from_num(), numeric(), numeric_sign(), numeric_uminus(), and set_var_from_num().

#define NUMERIC_SIGN_MASK   0xC000

Definition at line 153 of file numeric.c.

#define NUMERIC_WEIGHT (   n  ) 
Value:
(NUMERIC_IS_SHORT((n)) ? \
    (((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \
        ~NUMERIC_SHORT_WEIGHT_MASK : 0) \
     | ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \
    : ((n)->choice.n_long.n_weight))

Definition at line 203 of file numeric.c.

Referenced by cmp_numerics(), hash_numeric(), init_var_from_num(), make_result(), numeric(), and set_var_from_num().


Typedef Documentation

Definition at line 89 of file numeric.c.

typedef struct NumericVar NumericVar

Function Documentation

static void add_abs ( NumericVar var1,
NumericVar var2,
NumericVar result 
) [static]

Definition at line 5811 of file numeric.c.

References Assert, NumericVar::buf, digitbuf_alloc, digitbuf_free, NumericVar::digits, NumericVar::dscale, Max, NBASE, NumericVar::ndigits, strip_var(), and NumericVar::weight.

Referenced by add_var(), and sub_var().

{
    NumericDigit *res_buf;
    NumericDigit *res_digits;
    int         res_ndigits;
    int         res_weight;
    int         res_rscale,
                rscale1,
                rscale2;
    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_dscale = Max(var1->dscale, var2->dscale);

    /* Note: here we are figuring rscale in base-NBASE digits */
    rscale1 = var1->ndigits - var1->weight - 1;
    rscale2 = var2->ndigits - var2->weight - 1;
    res_rscale = Max(rscale1, rscale2);

    res_ndigits = res_rscale + res_weight + 1;
    if (res_ndigits <= 0)
        res_ndigits = 1;

    res_buf = digitbuf_alloc(res_ndigits + 1);
    res_buf[0] = 0;             /* spare digit for later rounding */
    res_digits = res_buf + 1;

    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 >= NBASE)
        {
            res_digits[i] = carry - NBASE;
            carry = 1;
        }
        else
        {
            res_digits[i] = carry;
            carry = 0;
        }
    }

    Assert(carry == 0);         /* else we failed to allow for carry out */

    digitbuf_free(result->buf);
    result->ndigits = res_ndigits;
    result->buf = res_buf;
    result->digits = res_digits;
    result->weight = res_weight;
    result->dscale = res_dscale;

    /* Remove leading/trailing zeroes */
    strip_var(result);
}

static void add_var ( NumericVar var1,
NumericVar var2,
NumericVar result 
) [static]

Definition at line 4073 of file numeric.c.

References add_abs(), cmp_abs(), NumericVar::dscale, Max, NUMERIC_POS, NumericVar::sign, sub_abs(), and zero_var().

Referenced by ceil_var(), compute_bucket(), exp_var_internal(), ln_var(), numeric_add(), numeric_inc(), sqrt_var(), and width_bucket_numeric().

{
    /*
     * 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))
             */
            add_abs(var1, var2, result);
            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->dscale = Max(var1->dscale, var2->dscale);
                    break;

                case 1:
                    /* ----------
                     * ABS(var1) > ABS(var2)
                     * result = +(ABS(var1) - ABS(var2))
                     * ----------
                     */
                    sub_abs(var1, var2, result);
                    result->sign = NUMERIC_POS;
                    break;

                case -1:
                    /* ----------
                     * ABS(var1) < ABS(var2)
                     * result = -(ABS(var2) - ABS(var1))
                     * ----------
                     */
                    sub_abs(var2, var1, result);
                    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->dscale = Max(var1->dscale, var2->dscale);
                    break;

                case 1:
                    /* ----------
                     * ABS(var1) > ABS(var2)
                     * result = -(ABS(var1) - ABS(var2))
                     * ----------
                     */
                    sub_abs(var1, var2, result);
                    result->sign = NUMERIC_NEG;
                    break;

                case -1:
                    /* ----------
                     * ABS(var1) < ABS(var2)
                     * result = +(ABS(var2) - ABS(var1))
                     * ----------
                     */
                    sub_abs(var2, var1, result);
                    result->sign = NUMERIC_POS;
                    break;
            }
        }
        else
        {
            /* ----------
             * Both are negative
             * result = -(ABS(var1) + ABS(var2))
             * ----------
             */
            add_abs(var1, var2, result);
            result->sign = NUMERIC_NEG;
        }
    }
}

static void alloc_var ( NumericVar var,
int  ndigits 
) [static]

Definition at line 3157 of file numeric.c.

References NumericVar::buf, digitbuf_alloc, digitbuf_free, NumericVar::digits, and NumericVar::ndigits.

Referenced by div_var(), div_var_fast(), int8_to_numericvar(), mul_var(), numeric_recv(), set_var_from_num(), set_var_from_str(), and sqrt_var().

{
    digitbuf_free(var->buf);
    var->buf = digitbuf_alloc(ndigits + 1);
    var->buf[0] = 0;            /* spare digit for rounding */
    var->digits = var->buf + 1;
    var->ndigits = ndigits;
}

static void apply_typmod ( NumericVar var,
int32  typmod 
) [static]

Definition at line 3764 of file numeric.c.

References DEC_DIGITS, NumericVar::digits, ereport, errcode(), errdetail(), errmsg(), ERROR, maxdigits, NumericVar::ndigits, round_var(), scale, VARHDRSZ, and NumericVar::weight.

Referenced by numeric(), numeric_in(), and numeric_recv().

{
    int         precision;
    int         scale;
    int         maxdigits;
    int         ddigits;
    int         i;

    /* Do nothing if we have a default typmod (-1) */
    if (typmod < (int32) (VARHDRSZ))
        return;

    typmod -= VARHDRSZ;
    precision = (typmod >> 16) & 0xffff;
    scale = typmod & 0xffff;
    maxdigits = precision - scale;

    /* Round to target scale (and set var->dscale) */
    round_var(var, scale);

    /*
     * Check for overflow - note we can't do this before rounding, because
     * rounding could raise the weight.  Also note that the var's weight could
     * be inflated by leading zeroes, which will be stripped before storage
     * but perhaps might not have been yet. In any case, we must recognize a
     * true zero, whose weight doesn't mean anything.
     */
    ddigits = (var->weight + 1) * DEC_DIGITS;
    if (ddigits > maxdigits)
    {
        /* Determine true weight; and check for all-zero result */
        for (i = 0; i < var->ndigits; i++)
        {
            NumericDigit dig = var->digits[i];

            if (dig)
            {
                /* Adjust for any high-order decimal zero digits */
#if DEC_DIGITS == 4
                if (dig < 10)
                    ddigits -= 3;
                else if (dig < 100)
                    ddigits -= 2;
                else if (dig < 1000)
                    ddigits -= 1;
#elif DEC_DIGITS == 2
                if (dig < 10)
                    ddigits -= 1;
#elif DEC_DIGITS == 1
                /* no adjustment */
#else
#error unsupported NBASE
#endif
                if (ddigits > maxdigits)
                    ereport(ERROR,
                            (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                             errmsg("numeric field overflow"),
                             errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.",
                                       precision, scale,
                    /* Display 10^0 as 1 */
                                       maxdigits ? "10^" : "",
                                       maxdigits ? maxdigits : 1
                                       )));
                break;
            }
            ddigits -= DEC_DIGITS;
        }
    }
}

static void ceil_var ( NumericVar var,
NumericVar result 
) [static]

Definition at line 5114 of file numeric.c.

References add_var(), cmp_var(), free_var(), init_var, NUMERIC_POS, set_var_from_var(), NumericVar::sign, and trunc_var().

Referenced by numeric_ceil().

{
    NumericVar  tmp;

    init_var(&tmp);
    set_var_from_var(var, &tmp);

    trunc_var(&tmp, 0);

    if (var->sign == NUMERIC_POS && cmp_var(var, &tmp) != 0)
        add_var(&tmp, &const_one, &tmp);

    set_var_from_var(&tmp, result);
    free_var(&tmp);
}

static int cmp_abs ( NumericVar var1,
NumericVar var2 
) [static]

Definition at line 5733 of file numeric.c.

References cmp_abs_common(), NumericVar::digits, NumericVar::ndigits, and NumericVar::weight.

Referenced by add_var(), and sub_var().

{
    return cmp_abs_common(var1->digits, var1->ndigits, var1->weight,
                          var2->digits, var2->ndigits, var2->weight);
}

static int cmp_abs_common ( const NumericDigit var1digits,
int  var1ndigits,
int  var1weight,
const NumericDigit var2digits,
int  var2ndigits,
int  var2weight 
) [static]

Definition at line 5747 of file numeric.c.

Referenced by cmp_abs(), and cmp_var_common().

{
    int         i1 = 0;
    int         i2 = 0;

    /* Check any digits before the first common digit */

    while (var1weight > var2weight && i1 < var1ndigits)
    {
        if (var1digits[i1++] != 0)
            return 1;
        var1weight--;
    }
    while (var2weight > var1weight && i2 < var2ndigits)
    {
        if (var2digits[i2++] != 0)
            return -1;
        var2weight--;
    }

    /* At this point, either w1 == w2 or we've run out of digits */

    if (var1weight == var2weight)
    {
        while (i1 < var1ndigits && i2 < var2ndigits)
        {
            int         stat = var1digits[i1++] - var2digits[i2++];

            if (stat)
            {
                if (stat > 0)
                    return 1;
                return -1;
            }
        }
    }

    /*
     * At this point, we've run out of digits on one side or the other; so any
     * remaining nonzero digits imply that side is larger
     */
    while (i1 < var1ndigits)
    {
        if (var1digits[i1++] != 0)
            return 1;
    }
    while (i2 < var2ndigits)
    {
        if (var2digits[i2++] != 0)
            return -1;
    }

    return 0;
}

static int cmp_numerics ( Numeric  num1,
Numeric  num2 
) [static]

Definition at line 1426 of file numeric.c.

References cmp_var_common(), NUMERIC_DIGITS, NUMERIC_IS_NAN, NUMERIC_NDIGITS, NUMERIC_SIGN, and NUMERIC_WEIGHT.

Referenced by numeric_cmp(), numeric_eq(), numeric_ge(), numeric_gt(), numeric_larger(), numeric_le(), numeric_lt(), numeric_ne(), numeric_smaller(), and width_bucket_numeric().

{
    int         result;

    /*
     * We consider all NANs to be equal and larger than any non-NAN. This is
     * somewhat arbitrary; the important thing is to have a consistent sort
     * order.
     */
    if (NUMERIC_IS_NAN(num1))
    {
        if (NUMERIC_IS_NAN(num2))
            result = 0;         /* NAN = NAN */
        else
            result = 1;         /* NAN > non-NAN */
    }
    else if (NUMERIC_IS_NAN(num2))
    {
        result = -1;            /* non-NAN < NAN */
    }
    else
    {
        result = cmp_var_common(NUMERIC_DIGITS(num1), NUMERIC_NDIGITS(num1),
                                NUMERIC_WEIGHT(num1), NUMERIC_SIGN(num1),
                                NUMERIC_DIGITS(num2), NUMERIC_NDIGITS(num2),
                                NUMERIC_WEIGHT(num2), NUMERIC_SIGN(num2));
    }

    return result;
}

static int cmp_var ( NumericVar var1,
NumericVar var2 
) [static]
static int cmp_var_common ( const NumericDigit var1digits,
int  var1ndigits,
int  var1weight,
int  var1sign,
const NumericDigit var2digits,
int  var2ndigits,
int  var2weight,
int  var2sign 
) [static]

Definition at line 4030 of file numeric.c.

References cmp_abs_common(), NUMERIC_NEG, and NUMERIC_POS.

Referenced by cmp_numerics(), and cmp_var().

{
    if (var1ndigits == 0)
    {
        if (var2ndigits == 0)
            return 0;
        if (var2sign == NUMERIC_NEG)
            return 1;
        return -1;
    }
    if (var2ndigits == 0)
    {
        if (var1sign == NUMERIC_POS)
            return 1;
        return -1;
    }

    if (var1sign == NUMERIC_POS)
    {
        if (var2sign == NUMERIC_NEG)
            return 1;
        return cmp_abs_common(var1digits, var1ndigits, var1weight,
                              var2digits, var2ndigits, var2weight);
    }

    if (var2sign == NUMERIC_POS)
        return -1;

    return cmp_abs_common(var2digits, var2ndigits, var2weight,
                          var1digits, var1ndigits, var1weight);
}

static void compute_bucket ( Numeric  operand,
Numeric  bound1,
Numeric  bound2,
NumericVar count_var,
NumericVar result_var 
) [static]

Definition at line 1272 of file numeric.c.

References add_var(), cmp_var(), div_var(), NumericVar::dscale, floor_var(), free_var(), init_var_from_num(), mul_var(), select_div_scale(), and sub_var().

Referenced by width_bucket_numeric().

{
    NumericVar  bound1_var;
    NumericVar  bound2_var;
    NumericVar  operand_var;

    init_var_from_num(bound1, &bound1_var);
    init_var_from_num(bound2, &bound2_var);
    init_var_from_num(operand, &operand_var);

    if (cmp_var(&bound1_var, &bound2_var) < 0)
    {
        sub_var(&operand_var, &bound1_var, &operand_var);
        sub_var(&bound2_var, &bound1_var, &bound2_var);
        div_var(&operand_var, &bound2_var, result_var,
                select_div_scale(&operand_var, &bound2_var), true);
    }
    else
    {
        sub_var(&bound1_var, &operand_var, &operand_var);
        sub_var(&bound1_var, &bound2_var, &bound1_var);
        div_var(&operand_var, &bound1_var, result_var,
                select_div_scale(&operand_var, &bound1_var), true);
    }

    mul_var(result_var, count_var, result_var,
            result_var->dscale + count_var->dscale);
    add_var(result_var, &const_one, result_var);
    floor_var(result_var, result_var);

    free_var(&bound1_var);
    free_var(&bound2_var);
    free_var(&operand_var);
}

static void div_var ( NumericVar var1,
NumericVar var2,
NumericVar result,
int  rscale,
bool  round 
) [static]

Definition at line 4482 of file numeric.c.

References alloc_var(), Assert, DEC_DIGITS, NumericVar::digits, NumericVar::dscale, ereport, errcode(), errmsg(), ERROR, HALF_NBASE, Max, NBASE, NumericVar::ndigits, palloc0(), pfree(), round_var(), NumericVar::sign, strip_var(), trunc_var(), NumericVar::weight, and zero_var().

Referenced by compute_bucket(), get_str_from_var_sci(), mod_var(), numeric_div(), numeric_div_trunc(), numeric_stddev_internal(), and power_var_int().

{
    int         div_ndigits;
    int         res_ndigits;
    int         res_sign;
    int         res_weight;
    int         carry;
    int         borrow;
    int         divisor1;
    int         divisor2;
    NumericDigit *dividend;
    NumericDigit *divisor;
    NumericDigit *res_digits;
    int         i;
    int         j;

    /* copy these values into local vars for speed in inner loop */
    int         var1ndigits = var1->ndigits;
    int         var2ndigits = var2->ndigits;

    /*
     * First of all division by zero check; we must not be handed an
     * unnormalized divisor.
     */
    if (var2ndigits == 0 || var2->digits[0] == 0)
        ereport(ERROR,
                (errcode(ERRCODE_DIVISION_BY_ZERO),
                 errmsg("division by zero")));

    /*
     * Now result zero check
     */
    if (var1ndigits == 0)
    {
        zero_var(result);
        result->dscale = rscale;
        return;
    }

    /*
     * Determine the result sign, weight and number of digits to calculate.
     * The weight figured here is correct if the emitted quotient has no
     * leading zero digits; otherwise strip_var() will fix things up.
     */
    if (var1->sign == var2->sign)
        res_sign = NUMERIC_POS;
    else
        res_sign = NUMERIC_NEG;
    res_weight = var1->weight - var2->weight;
    /* The number of accurate result digits we need to produce: */
    res_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS;
    /* ... but always at least 1 */
    res_ndigits = Max(res_ndigits, 1);
    /* If rounding needed, figure one more digit to ensure correct result */
    if (round)
        res_ndigits++;

    /*
     * The working dividend normally requires res_ndigits + var2ndigits
     * digits, but make it at least var1ndigits so we can load all of var1
     * into it.  (There will be an additional digit dividend[0] in the
     * dividend space, but for consistency with Knuth's notation we don't
     * count that in div_ndigits.)
     */
    div_ndigits = res_ndigits + var2ndigits;
    div_ndigits = Max(div_ndigits, var1ndigits);

    /*
     * We need a workspace with room for the working dividend (div_ndigits+1
     * digits) plus room for the possibly-normalized divisor (var2ndigits
     * digits).  It is convenient also to have a zero at divisor[0] with the
     * actual divisor data in divisor[1 .. var2ndigits].  Transferring the
     * digits into the workspace also allows us to realloc the result (which
     * might be the same as either input var) before we begin the main loop.
     * Note that we use palloc0 to ensure that divisor[0], dividend[0], and
     * any additional dividend positions beyond var1ndigits, start out 0.
     */
    dividend = (NumericDigit *)
        palloc0((div_ndigits + var2ndigits + 2) * sizeof(NumericDigit));
    divisor = dividend + (div_ndigits + 1);
    memcpy(dividend + 1, var1->digits, var1ndigits * sizeof(NumericDigit));
    memcpy(divisor + 1, var2->digits, var2ndigits * sizeof(NumericDigit));

    /*
     * Now we can realloc the result to hold the generated quotient digits.
     */
    alloc_var(result, res_ndigits);
    res_digits = result->digits;

    if (var2ndigits == 1)
    {
        /*
         * If there's only a single divisor digit, we can use a fast path (cf.
         * Knuth section 4.3.1 exercise 16).
         */
        divisor1 = divisor[1];
        carry = 0;
        for (i = 0; i < res_ndigits; i++)
        {
            carry = carry * NBASE + dividend[i + 1];
            res_digits[i] = carry / divisor1;
            carry = carry % divisor1;
        }
    }
    else
    {
        /*
         * The full multiple-place algorithm is taken from Knuth volume 2,
         * Algorithm 4.3.1D.
         *
         * We need the first divisor digit to be >= NBASE/2.  If it isn't,
         * make it so by scaling up both the divisor and dividend by the
         * factor "d".  (The reason for allocating dividend[0] above is to
         * leave room for possible carry here.)
         */
        if (divisor[1] < HALF_NBASE)
        {
            int         d = NBASE / (divisor[1] + 1);

            carry = 0;
            for (i = var2ndigits; i > 0; i--)
            {
                carry += divisor[i] * d;
                divisor[i] = carry % NBASE;
                carry = carry / NBASE;
            }
            Assert(carry == 0);
            carry = 0;
            /* at this point only var1ndigits of dividend can be nonzero */
            for (i = var1ndigits; i >= 0; i--)
            {
                carry += dividend[i] * d;
                dividend[i] = carry % NBASE;
                carry = carry / NBASE;
            }
            Assert(carry == 0);
            Assert(divisor[1] >= HALF_NBASE);
        }
        /* First 2 divisor digits are used repeatedly in main loop */
        divisor1 = divisor[1];
        divisor2 = divisor[2];

        /*
         * Begin the main loop.  Each iteration of this loop produces the j'th
         * quotient digit by dividing dividend[j .. j + var2ndigits] by the
         * divisor; this is essentially the same as the common manual
         * procedure for long division.
         */
        for (j = 0; j < res_ndigits; j++)
        {
            /* Estimate quotient digit from the first two dividend digits */
            int         next2digits = dividend[j] * NBASE + dividend[j + 1];
            int         qhat;

            /*
             * If next2digits are 0, then quotient digit must be 0 and there's
             * no need to adjust the working dividend.  It's worth testing
             * here to fall out ASAP when processing trailing zeroes in a
             * dividend.
             */
            if (next2digits == 0)
            {
                res_digits[j] = 0;
                continue;
            }

            if (dividend[j] == divisor1)
                qhat = NBASE - 1;
            else
                qhat = next2digits / divisor1;

            /*
             * Adjust quotient digit if it's too large.  Knuth proves that
             * after this step, the quotient digit will be either correct or
             * just one too large.  (Note: it's OK to use dividend[j+2] here
             * because we know the divisor length is at least 2.)
             */
            while (divisor2 * qhat >
                   (next2digits - qhat * divisor1) * NBASE + dividend[j + 2])
                qhat--;

            /* As above, need do nothing more when quotient digit is 0 */
            if (qhat > 0)
            {
                /*
                 * Multiply the divisor by qhat, and subtract that from the
                 * working dividend.  "carry" tracks the multiplication,
                 * "borrow" the subtraction (could we fold these together?)
                 */
                carry = 0;
                borrow = 0;
                for (i = var2ndigits; i >= 0; i--)
                {
                    carry += divisor[i] * qhat;
                    borrow -= carry % NBASE;
                    carry = carry / NBASE;
                    borrow += dividend[j + i];
                    if (borrow < 0)
                    {
                        dividend[j + i] = borrow + NBASE;
                        borrow = -1;
                    }
                    else
                    {
                        dividend[j + i] = borrow;
                        borrow = 0;
                    }
                }
                Assert(carry == 0);

                /*
                 * If we got a borrow out of the top dividend digit, then
                 * indeed qhat was one too large.  Fix it, and add back the
                 * divisor to correct the working dividend.  (Knuth proves
                 * that this will occur only about 3/NBASE of the time; hence,
                 * it's a good idea to test this code with small NBASE to be
                 * sure this section gets exercised.)
                 */
                if (borrow)
                {
                    qhat--;
                    carry = 0;
                    for (i = var2ndigits; i >= 0; i--)
                    {
                        carry += dividend[j + i] + divisor[i];
                        if (carry >= NBASE)
                        {
                            dividend[j + i] = carry - NBASE;
                            carry = 1;
                        }
                        else
                        {
                            dividend[j + i] = carry;
                            carry = 0;
                        }
                    }
                    /* A carry should occur here to cancel the borrow above */
                    Assert(carry == 1);
                }
            }

            /* And we're done with this quotient digit */
            res_digits[j] = qhat;
        }
    }

    pfree(dividend);

    /*
     * Finally, round or truncate the result to the requested precision.
     */
    result->weight = res_weight;
    result->sign = res_sign;

    /* Round or truncate to target rscale (and set result->dscale) */
    if (round)
        round_var(result, rscale);
    else
        trunc_var(result, rscale);

    /* Strip leading and trailing zeroes */
    strip_var(result);
}

static void div_var_fast ( NumericVar var1,
NumericVar var2,
NumericVar result,
int  rscale,
bool  round 
) [static]

Definition at line 4761 of file numeric.c.

References Abs, alloc_var(), Assert, DEC_DIGITS, NumericVar::digits, DIV_GUARD_DIGITS, NumericVar::dscale, ereport, errcode(), errmsg(), ERROR, Max, Min, NBASE, NumericVar::ndigits, palloc0(), pfree(), round_var(), NumericVar::sign, strip_var(), trunc_var(), NumericVar::weight, and zero_var().

Referenced by exp_var(), exp_var_internal(), ln_var(), log_var(), power_var_int(), and sqrt_var().

{
    int         div_ndigits;
    int         res_sign;
    int         res_weight;
    int        *div;
    int         qdigit;
    int         carry;
    int         maxdiv;
    int         newdig;
    NumericDigit *res_digits;
    double      fdividend,
                fdivisor,
                fdivisorinverse,
                fquotient;
    int         qi;
    int         i;

    /* 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;

    /*
     * First of all division by zero check; we must not be handed an
     * unnormalized divisor.
     */
    if (var2ndigits == 0 || var2digits[0] == 0)
        ereport(ERROR,
                (errcode(ERRCODE_DIVISION_BY_ZERO),
                 errmsg("division by zero")));

    /*
     * Now result zero check
     */
    if (var1ndigits == 0)
    {
        zero_var(result);
        result->dscale = rscale;
        return;
    }

    /*
     * 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;
    /* The number of accurate result digits we need to produce: */
    div_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS;
    /* Add guard digits for roundoff error */
    div_ndigits += DIV_GUARD_DIGITS;
    if (div_ndigits < DIV_GUARD_DIGITS)
        div_ndigits = DIV_GUARD_DIGITS;
    /* Must be at least var1ndigits, too, to simplify data-loading loop */
    if (div_ndigits < var1ndigits)
        div_ndigits = var1ndigits;

    /*
     * We do the arithmetic in an array "div[]" of signed int's.  Since
     * INT_MAX is noticeably larger than NBASE*NBASE, this gives us headroom
     * to avoid normalizing carries immediately.
     *
     * We start with div[] containing one zero digit followed by the
     * dividend's digits (plus appended zeroes to reach the desired precision
     * including guard digits).  Each step of the main loop computes an
     * (approximate) quotient digit and stores it into div[], removing one
     * position of dividend space.  A final pass of carry propagation takes
     * care of any mistaken quotient digits.
     */
    div = (int *) palloc0((div_ndigits + 1) * sizeof(int));
    for (i = 0; i < var1ndigits; i++)
        div[i + 1] = var1digits[i];

    /*
     * We estimate each quotient digit using floating-point arithmetic, taking
     * the first four digits of the (current) dividend and divisor. This must
     * be float to avoid overflow.
     */
    fdivisor = (double) var2digits[0];
    for (i = 1; i < 4; i++)
    {
        fdivisor *= NBASE;
        if (i < var2ndigits)
            fdivisor += (double) var2digits[i];
    }
    fdivisorinverse = 1.0 / fdivisor;

    /*
     * maxdiv tracks the maximum possible absolute value of any div[] entry;
     * when this threatens to exceed INT_MAX, we take the time to propagate
     * carries.  To avoid overflow in maxdiv itself, it actually represents
     * the max possible abs. value divided by NBASE-1.
     */
    maxdiv = 1;

    /*
     * Outer loop computes next quotient digit, which will go into div[qi]
     */
    for (qi = 0; qi < div_ndigits; qi++)
    {
        /* Approximate the current dividend value */
        fdividend = (double) div[qi];
        for (i = 1; i < 4; i++)
        {
            fdividend *= NBASE;
            if (qi + i <= div_ndigits)
                fdividend += (double) div[qi + i];
        }
        /* Compute the (approximate) quotient digit */
        fquotient = fdividend * fdivisorinverse;
        qdigit = (fquotient >= 0.0) ? ((int) fquotient) :
            (((int) fquotient) - 1);    /* truncate towards -infinity */

        if (qdigit != 0)
        {
            /* Do we need to normalize now? */
            maxdiv += Abs(qdigit);
            if (maxdiv > INT_MAX / (NBASE - 1))
            {
                /* Yes, do it */
                carry = 0;
                for (i = div_ndigits; i > qi; i--)
                {
                    newdig = div[i] + carry;
                    if (newdig < 0)
                    {
                        carry = -((-newdig - 1) / NBASE) - 1;
                        newdig -= carry * NBASE;
                    }
                    else if (newdig >= NBASE)
                    {
                        carry = newdig / NBASE;
                        newdig -= carry * NBASE;
                    }
                    else
                        carry = 0;
                    div[i] = newdig;
                }
                newdig = div[qi] + carry;
                div[qi] = newdig;

                /*
                 * All the div[] digits except possibly div[qi] are now in the
                 * range 0..NBASE-1.
                 */
                maxdiv = Abs(newdig) / (NBASE - 1);
                maxdiv = Max(maxdiv, 1);

                /*
                 * Recompute the quotient digit since new info may have
                 * propagated into the top four dividend digits
                 */
                fdividend = (double) div[qi];
                for (i = 1; i < 4; i++)
                {
                    fdividend *= NBASE;
                    if (qi + i <= div_ndigits)
                        fdividend += (double) div[qi + i];
                }
                /* Compute the (approximate) quotient digit */
                fquotient = fdividend * fdivisorinverse;
                qdigit = (fquotient >= 0.0) ? ((int) fquotient) :
                    (((int) fquotient) - 1);    /* truncate towards -infinity */
                maxdiv += Abs(qdigit);
            }

            /* Subtract off the appropriate multiple of the divisor */
            if (qdigit != 0)
            {
                int         istop = Min(var2ndigits, div_ndigits - qi + 1);

                for (i = 0; i < istop; i++)
                    div[qi + i] -= qdigit * var2digits[i];
            }
        }

        /*
         * The dividend digit we are about to replace might still be nonzero.
         * Fold it into the next digit position.  We don't need to worry about
         * overflow here since this should nearly cancel with the subtraction
         * of the divisor.
         */
        div[qi + 1] += div[qi] * NBASE;

        div[qi] = qdigit;
    }

    /*
     * Approximate and store the last quotient digit (div[div_ndigits])
     */
    fdividend = (double) div[qi];
    for (i = 1; i < 4; i++)
        fdividend *= NBASE;
    fquotient = fdividend * fdivisorinverse;
    qdigit = (fquotient >= 0.0) ? ((int) fquotient) :
        (((int) fquotient) - 1);    /* truncate towards -infinity */
    div[qi] = qdigit;

    /*
     * Now we do a final carry propagation pass to normalize the result, which
     * we combine with storing the result digits into the output. Note that
     * this is still done at full precision w/guard digits.
     */
    alloc_var(result, div_ndigits + 1);
    res_digits = result->digits;
    carry = 0;
    for (i = div_ndigits; i >= 0; i--)
    {
        newdig = div[i] + carry;
        if (newdig < 0)
        {
            carry = -((-newdig - 1) / NBASE) - 1;
            newdig -= carry * NBASE;
        }
        else if (newdig >= NBASE)
        {
            carry = newdig / NBASE;
            newdig -= carry * NBASE;
        }
        else
            carry = 0;
        res_digits[i] = newdig;
    }
    Assert(carry == 0);

    pfree(div);

    /*
     * Finally, round the result to the requested precision.
     */
    result->weight = res_weight;
    result->sign = res_sign;

    /* Round to target rscale (and set result->dscale) */
    if (round)
        round_var(result, rscale);
    else
        trunc_var(result, rscale);

    /* Strip leading and trailing zeroes */
    strip_var(result);
}

static ArrayType* do_numeric_accum ( ArrayType transarray,
Numeric  newval 
) [static]

Definition at line 2477 of file numeric.c.

References construct_array(), deconstruct_array(), DirectFunctionCall1, DirectFunctionCall2, elog, ERROR, NULL, numeric_add(), numeric_inc(), numeric_mul(), NumericGetDatum, and NUMERICOID.

Referenced by int2_accum(), int4_accum(), int8_accum(), and numeric_accum().

{
    Datum      *transdatums;
    int         ndatums;
    Datum       N,
                sumX,
                sumX2;
    ArrayType  *result;

    /* We assume the input is array of numeric */
    deconstruct_array(transarray,
                      NUMERICOID, -1, false, 'i',
                      &transdatums, NULL, &ndatums);
    if (ndatums != 3)
        elog(ERROR, "expected 3-element numeric array");
    N = transdatums[0];
    sumX = transdatums[1];
    sumX2 = transdatums[2];

    N = DirectFunctionCall1(numeric_inc, N);
    sumX = DirectFunctionCall2(numeric_add, sumX,
                               NumericGetDatum(newval));
    sumX2 = DirectFunctionCall2(numeric_add, sumX2,
                                DirectFunctionCall2(numeric_mul,
                                                    NumericGetDatum(newval),
                                                    NumericGetDatum(newval)));

    transdatums[0] = N;
    transdatums[1] = sumX;
    transdatums[2] = sumX2;

    result = construct_array(transdatums, 3,
                             NUMERICOID, -1, false, 'i');

    return result;
}

static ArrayType* do_numeric_avg_accum ( ArrayType transarray,
Numeric  newval 
) [static]

Definition at line 2518 of file numeric.c.

References construct_array(), deconstruct_array(), DirectFunctionCall1, DirectFunctionCall2, elog, ERROR, NULL, numeric_add(), numeric_inc(), NumericGetDatum, and NUMERICOID.

Referenced by int8_avg_accum(), and numeric_avg_accum().

{
    Datum      *transdatums;
    int         ndatums;
    Datum       N,
                sumX;
    ArrayType  *result;

    /* We assume the input is array of numeric */
    deconstruct_array(transarray,
                      NUMERICOID, -1, false, 'i',
                      &transdatums, NULL, &ndatums);
    if (ndatums != 2)
        elog(ERROR, "expected 2-element numeric array");
    N = transdatums[0];
    sumX = transdatums[1];

    N = DirectFunctionCall1(numeric_inc, N);
    sumX = DirectFunctionCall2(numeric_add, sumX,
                               NumericGetDatum(newval));

    transdatums[0] = N;
    transdatums[1] = sumX;

    result = construct_array(transdatums, 2,
                             NUMERICOID, -1, false, 'i');

    return result;
}

static void exp_var ( NumericVar arg,
NumericVar result,
int  rscale 
) [static]

Definition at line 5234 of file numeric.c.

References NumericVar::digits, div_var_fast(), ereport, errcode(), errmsg(), ERROR, exp_var_internal(), free_var(), init_var, MUL_GUARD_DIGITS, mul_var(), NumericVar::ndigits, NUMERIC_MAX_RESULT_SCALE, NUMERIC_NEG, power_var_int(), round_var(), set_var_from_var(), NumericVar::sign, and NumericVar::weight.

Referenced by numeric_exp(), and power_var().

{
    NumericVar  x;
    int         xintval;
    bool        xneg = FALSE;
    int         local_rscale;

    /*----------
     * We separate the integral and fraction parts of x, then compute
     *      e^x = e^xint * e^xfrac
     * where e = exp(1) and e^xfrac = exp(xfrac) are computed by
     * exp_var_internal; the limited range of inputs allows that routine
     * to do a good job with a simple Taylor series.  Raising e^xint is
     * done by repeated multiplications in power_var_int.
     *----------
     */
    init_var(&x);

    set_var_from_var(arg, &x);

    if (x.sign == NUMERIC_NEG)
    {
        xneg = TRUE;
        x.sign = NUMERIC_POS;
    }

    /* Extract the integer part, remove it from x */
    xintval = 0;
    while (x.weight >= 0)
    {
        xintval *= NBASE;
        if (x.ndigits > 0)
        {
            xintval += x.digits[0];
            x.digits++;
            x.ndigits--;
        }
        x.weight--;
        /* Guard against overflow */
        if (xintval >= NUMERIC_MAX_RESULT_SCALE * 3)
            ereport(ERROR,
                    (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                     errmsg("argument for function \"exp\" too big")));
    }

    /* Select an appropriate scale for internal calculation */
    local_rscale = rscale + MUL_GUARD_DIGITS * 2;

    /* Compute e^xfrac */
    exp_var_internal(&x, result, local_rscale);

    /* If there's an integer part, multiply by e^xint */
    if (xintval > 0)
    {
        NumericVar  e;

        init_var(&e);
        exp_var_internal(&const_one, &e, local_rscale);
        power_var_int(&e, xintval, &e, local_rscale);
        mul_var(&e, result, result, local_rscale);
        free_var(&e);
    }

    /* Compensate for input sign, and round to requested rscale */
    if (xneg)
        div_var_fast(&const_one, result, result, rscale, true);
    else
        round_var(result, rscale);

    free_var(&x);
}

static void exp_var_internal ( NumericVar arg,
NumericVar result,
int  rscale 
) [static]

Definition at line 5316 of file numeric.c.

References add_var(), Assert, cmp_var(), div_var_fast(), NumericVar::dscale, free_var(), init_var, mul_var(), NumericVar::ndigits, NUMERIC_POS, set_var_from_var(), and NumericVar::sign.

Referenced by exp_var().

{
    NumericVar  x;
    NumericVar  xpow;
    NumericVar  ifac;
    NumericVar  elem;
    NumericVar  ni;
    int         ndiv2 = 0;
    int         local_rscale;

    init_var(&x);
    init_var(&xpow);
    init_var(&ifac);
    init_var(&elem);
    init_var(&ni);

    set_var_from_var(arg, &x);

    Assert(x.sign == NUMERIC_POS);

    local_rscale = rscale + 8;

    /* Reduce input into range 0 <= x <= 0.01 */
    while (cmp_var(&x, &const_zero_point_01) > 0)
    {
        ndiv2++;
        local_rscale++;
        mul_var(&x, &const_zero_point_five, &x, x.dscale + 1);
    }

    /*
     * Use the Taylor series
     *
     * exp(x) = 1 + x + x^2/2! + x^3/3! + ...
     *
     * Given the limited range of x, this should converge reasonably quickly.
     * We run the series until the terms fall below the local_rscale limit.
     */
    add_var(&const_one, &x, result);
    set_var_from_var(&x, &xpow);
    set_var_from_var(&const_one, &ifac);
    set_var_from_var(&const_one, &ni);

    for (;;)
    {
        add_var(&ni, &const_one, &ni);
        mul_var(&xpow, &x, &xpow, local_rscale);
        mul_var(&ifac, &ni, &ifac, 0);
        div_var_fast(&xpow, &ifac, &elem, local_rscale, true);

        if (elem.ndigits == 0)
            break;

        add_var(result, &elem, result);
    }

    /* Compensate for argument range reduction */
    while (ndiv2-- > 0)
        mul_var(result, result, result, local_rscale);

    free_var(&x);
    free_var(&xpow);
    free_var(&ifac);
    free_var(&elem);
    free_var(&ni);
}

Datum float4_numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 2417 of file numeric.c.

References buf, free_var(), init_var, make_result(), PG_GETARG_FLOAT4, PG_RETURN_NUMERIC, set_var_from_str(), and val.

{
    float4      val = PG_GETARG_FLOAT4(0);
    Numeric     res;
    NumericVar  result;
    char        buf[FLT_DIG + 100];

    if (isnan(val))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    sprintf(buf, "%.*g", FLT_DIG, val);

    init_var(&result);

    /* Assume we need not worry about leading/trailing spaces */
    (void) set_var_from_str(buf, buf, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum float8_numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 2355 of file numeric.c.

References buf, free_var(), init_var, make_result(), PG_GETARG_FLOAT8, PG_RETURN_NUMERIC, set_var_from_str(), and val.

{
    float8      val = PG_GETARG_FLOAT8(0);
    Numeric     res;
    NumericVar  result;
    char        buf[DBL_DIG + 100];

    if (isnan(val))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    sprintf(buf, "%.*g", DBL_DIG, val);

    init_var(&result);

    /* Assume we need not worry about leading/trailing spaces */
    (void) set_var_from_str(buf, buf, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

static void floor_var ( NumericVar var,
NumericVar result 
) [static]

Definition at line 5138 of file numeric.c.

References cmp_var(), free_var(), init_var, NUMERIC_NEG, set_var_from_var(), NumericVar::sign, sub_var(), and trunc_var().

Referenced by compute_bucket(), and numeric_floor().

{
    NumericVar  tmp;

    init_var(&tmp);
    set_var_from_var(var, &tmp);

    trunc_var(&tmp, 0);

    if (var->sign == NUMERIC_NEG && cmp_var(var, &tmp) != 0)
        sub_var(&tmp, &const_one, &tmp);

    set_var_from_var(&tmp, result);
    free_var(&tmp);
}

static void free_var ( NumericVar var  )  [static]
static char * get_str_from_var ( NumericVar var  )  [static]

Definition at line 3439 of file numeric.c.

References DEC_DIGITS, NumericVar::digits, NumericVar::dscale, NumericVar::ndigits, NUMERIC_NEG, palloc(), NumericVar::sign, and NumericVar::weight.

Referenced by get_str_from_var_sci(), numeric_out(), and numericvar_to_double_no_overflow().

{
    int         dscale;
    char       *str;
    char       *cp;
    char       *endcp;
    int         i;
    int         d;
    NumericDigit dig;

#if DEC_DIGITS > 1
    NumericDigit d1;
#endif

    dscale = var->dscale;

    /*
     * Allocate space for the result.
     *
     * i is set to the # of decimal digits before decimal point. dscale is the
     * # of decimal digits we will print after decimal point. We may generate
     * as many as DEC_DIGITS-1 excess digits at the end, and in addition we
     * need room for sign, decimal point, null terminator.
     */
    i = (var->weight + 1) * DEC_DIGITS;
    if (i <= 0)
        i = 1;

    str = palloc(i + dscale + DEC_DIGITS + 2);
    cp = str;

    /*
     * Output a dash for negative values
     */
    if (var->sign == NUMERIC_NEG)
        *cp++ = '-';

    /*
     * Output all digits before the decimal point
     */
    if (var->weight < 0)
    {
        d = var->weight + 1;
        *cp++ = '0';
    }
    else
    {
        for (d = 0; d <= var->weight; d++)
        {
            dig = (d < var->ndigits) ? var->digits[d] : 0;
            /* In the first digit, suppress extra leading decimal zeroes */
#if DEC_DIGITS == 4
            {
                bool        putit = (d > 0);

                d1 = dig / 1000;
                dig -= d1 * 1000;
                putit |= (d1 > 0);
                if (putit)
                    *cp++ = d1 + '0';
                d1 = dig / 100;
                dig -= d1 * 100;
                putit |= (d1 > 0);
                if (putit)
                    *cp++ = d1 + '0';
                d1 = dig / 10;
                dig -= d1 * 10;
                putit |= (d1 > 0);
                if (putit)
                    *cp++ = d1 + '0';
                *cp++ = dig + '0';
            }
#elif DEC_DIGITS == 2
            d1 = dig / 10;
            dig -= d1 * 10;
            if (d1 > 0 || d > 0)
                *cp++ = d1 + '0';
            *cp++ = dig + '0';
#elif DEC_DIGITS == 1
            *cp++ = dig + '0';
#else
#error unsupported NBASE
#endif
        }
    }

    /*
     * If requested, output a decimal point and all the digits that follow it.
     * We initially put out a multiple of DEC_DIGITS digits, then truncate if
     * needed.
     */
    if (dscale > 0)
    {
        *cp++ = '.';
        endcp = cp + dscale;
        for (i = 0; i < dscale; d++, i += DEC_DIGITS)
        {
            dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0;
#if DEC_DIGITS == 4
            d1 = dig / 1000;
            dig -= d1 * 1000;
            *cp++ = d1 + '0';
            d1 = dig / 100;
            dig -= d1 * 100;
            *cp++ = d1 + '0';
            d1 = dig / 10;
            dig -= d1 * 10;
            *cp++ = d1 + '0';
            *cp++ = dig + '0';
#elif DEC_DIGITS == 2
            d1 = dig / 10;
            dig -= d1 * 10;
            *cp++ = d1 + '0';
            *cp++ = dig + '0';
#elif DEC_DIGITS == 1
            *cp++ = dig + '0';
#else
#error unsupported NBASE
#endif
        }
        cp = endcp;
    }

    /*
     * terminate the string and return it
     */
    *cp = '\0';
    return str;
}

static char * get_str_from_var_sci ( NumericVar var,
int  rscale 
) [static]

Definition at line 3592 of file numeric.c.

References DEC_DIGITS, NumericVar::digits, div_var(), free_var(), get_str_from_var(), init_var, NumericVar::ndigits, palloc(), pfree(), power_var_int(), snprintf(), and NumericVar::weight.

Referenced by numeric_out_sci().

{
    int32       exponent;
    NumericVar  denominator;
    NumericVar  significand;
    int         denom_scale;
    size_t      len;
    char       *str;
    char       *sig_out;

    if (rscale < 0)
        rscale = 0;

    /*
     * Determine the exponent of this number in normalised form.
     *
     * This is the exponent required to represent the number with only one
     * significant digit before the decimal place.
     */
    if (var->ndigits > 0)
    {
        exponent = (var->weight + 1) * DEC_DIGITS;

        /*
         * Compensate for leading decimal zeroes in the first numeric digit by
         * decrementing the exponent.
         */
        exponent -= DEC_DIGITS - (int) log10(var->digits[0]);
    }
    else
    {
        /*
         * If var has no digits, then it must be zero.
         *
         * Zero doesn't technically have a meaningful exponent in normalised
         * notation, but we just display the exponent as zero for consistency
         * of output.
         */
        exponent = 0;
    }

    /*
     * The denominator is set to 10 raised to the power of the exponent.
     *
     * We then divide var by the denominator to get the significand, rounding
     * to rscale decimal digits in the process.
     */
    if (exponent < 0)
        denom_scale = -exponent;
    else
        denom_scale = 0;

    init_var(&denominator);
    init_var(&significand);

    power_var_int(&const_ten, exponent, &denominator, denom_scale);
    div_var(var, &denominator, &significand, rscale, true);
    sig_out = get_str_from_var(&significand);

    free_var(&denominator);
    free_var(&significand);

    /*
     * Allocate space for the result.
     *
     * In addition to the significand, we need room for the exponent
     * decoration ("e"), the sign of the exponent, up to 10 digits for the
     * exponent itself, and of course the null terminator.
     */
    len = strlen(sig_out) + 13;
    str = palloc(len);
    snprintf(str, len, "%se%+03d", sig_out, exponent);

    pfree(sig_out);

    return str;
}

Datum hash_numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 1458 of file numeric.c.

References Assert, digits, hash_any(), i, NUMERIC_DIGITS, NUMERIC_IS_NAN, NUMERIC_NDIGITS, NUMERIC_WEIGHT, PG_GETARG_NUMERIC, PG_RETURN_DATUM, and PG_RETURN_UINT32.

{
    Numeric     key = PG_GETARG_NUMERIC(0);
    Datum       digit_hash;
    Datum       result;
    int         weight;
    int         start_offset;
    int         end_offset;
    int         i;
    int         hash_len;
    NumericDigit *digits;

    /* If it's NaN, don't try to hash the rest of the fields */
    if (NUMERIC_IS_NAN(key))
        PG_RETURN_UINT32(0);

    weight = NUMERIC_WEIGHT(key);
    start_offset = 0;
    end_offset = 0;

    /*
     * Omit any leading or trailing zeros from the input to the hash. The
     * numeric implementation *should* guarantee that leading and trailing
     * zeros are suppressed, but we're paranoid. Note that we measure the
     * starting and ending offsets in units of NumericDigits, not bytes.
     */
    digits = NUMERIC_DIGITS(key);
    for (i = 0; i < NUMERIC_NDIGITS(key); i++)
    {
        if (digits[i] != (NumericDigit) 0)
            break;

        start_offset++;

        /*
         * The weight is effectively the # of digits before the decimal point,
         * so decrement it for each leading zero we skip.
         */
        weight--;
    }

    /*
     * If there are no non-zero digits, then the value of the number is zero,
     * regardless of any other fields.
     */
    if (NUMERIC_NDIGITS(key) == start_offset)
        PG_RETURN_UINT32(-1);

    for (i = NUMERIC_NDIGITS(key) - 1; i >= 0; i--)
    {
        if (digits[i] != (NumericDigit) 0)
            break;

        end_offset++;
    }

    /* If we get here, there should be at least one non-zero digit */
    Assert(start_offset + end_offset < NUMERIC_NDIGITS(key));

    /*
     * Note that we don't hash on the Numeric's scale, since two numerics can
     * compare equal but have different scales. We also don't hash on the
     * sign, although we could: since a sign difference implies inequality,
     * this shouldn't affect correctness.
     */
    hash_len = NUMERIC_NDIGITS(key) - start_offset - end_offset;
    digit_hash = hash_any((unsigned char *) (NUMERIC_DIGITS(key) + start_offset),
                          hash_len * sizeof(NumericDigit));

    /* Mix in the weight, via XOR */
    result = digit_hash ^ weight;

    PG_RETURN_DATUM(result);
}

static void init_var_from_num ( Numeric  num,
NumericVar dest 
) [static]
Datum int2_accum ( PG_FUNCTION_ARGS   ) 
Datum int2_avg_accum ( PG_FUNCTION_ARGS   ) 

Definition at line 2981 of file numeric.c.

References AggCheckCallContext(), ARR_DATA_PTR, ARR_HASNULL, ARR_OVERHEAD_NONULLS, ARR_SIZE, Int8TransTypeData::count, elog, ERROR, NULL, PG_GETARG_ARRAYTYPE_P, PG_GETARG_ARRAYTYPE_P_COPY, PG_GETARG_INT16, PG_RETURN_ARRAYTYPE_P, and Int8TransTypeData::sum.

{
    ArrayType  *transarray;
    int16       newval = PG_GETARG_INT16(1);
    Int8TransTypeData *transdata;

    /*
     * If we're invoked as an aggregate, we can cheat and modify our first
     * parameter in-place to reduce palloc overhead. Otherwise we need to make
     * a copy of it before scribbling on it.
     */
    if (AggCheckCallContext(fcinfo, NULL))
        transarray = PG_GETARG_ARRAYTYPE_P(0);
    else
        transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

    if (ARR_HASNULL(transarray) ||
        ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
        elog(ERROR, "expected 2-element int8 array");

    transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
    transdata->count++;
    transdata->sum += newval;

    PG_RETURN_ARRAYTYPE_P(transarray);
}

Datum int2_numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 2301 of file numeric.c.

References free_var(), init_var, int8_to_numericvar(), make_result(), PG_GETARG_INT16, PG_RETURN_NUMERIC, and val.

Referenced by int2_accum().

{
    int16       val = PG_GETARG_INT16(0);
    Numeric     res;
    NumericVar  result;

    init_var(&result);

    int8_to_numericvar((int64) val, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum int2_sum ( PG_FUNCTION_ARGS   ) 

Definition at line 2836 of file numeric.c.

References AggCheckCallContext(), NULL, PG_ARGISNULL, PG_GETARG_INT16, PG_GETARG_INT64, PG_GETARG_POINTER, PG_RETURN_INT64, PG_RETURN_NULL, and PG_RETURN_POINTER.

{
    int64       newval;

    if (PG_ARGISNULL(0))
    {
        /* No non-null input seen so far... */
        if (PG_ARGISNULL(1))
            PG_RETURN_NULL();   /* still no non-null */
        /* This is the first non-null input. */
        newval = (int64) PG_GETARG_INT16(1);
        PG_RETURN_INT64(newval);
    }

    /*
     * If we're invoked as an aggregate, we can cheat and modify our first
     * parameter in-place to avoid palloc overhead. If not, we need to return
     * the new value of the transition variable. (If int8 is pass-by-value,
     * then of course this is useless as well as incorrect, so just ifdef it
     * out.)
     */
#ifndef USE_FLOAT8_BYVAL        /* controls int8 too */
    if (AggCheckCallContext(fcinfo, NULL))
    {
        int64      *oldsum = (int64 *) PG_GETARG_POINTER(0);

        /* Leave the running sum unchanged in the new input is null */
        if (!PG_ARGISNULL(1))
            *oldsum = *oldsum + (int64) PG_GETARG_INT16(1);

        PG_RETURN_POINTER(oldsum);
    }
    else
#endif
    {
        int64       oldsum = PG_GETARG_INT64(0);

        /* Leave sum unchanged if new input is null. */
        if (PG_ARGISNULL(1))
            PG_RETURN_INT64(oldsum);

        /* OK to do the addition. */
        newval = oldsum + (int64) PG_GETARG_INT16(1);

        PG_RETURN_INT64(newval);
    }
}

Datum int4_accum ( PG_FUNCTION_ARGS   ) 
Datum int4_avg_accum ( PG_FUNCTION_ARGS   ) 

Definition at line 3009 of file numeric.c.

References AggCheckCallContext(), ARR_DATA_PTR, ARR_HASNULL, ARR_OVERHEAD_NONULLS, ARR_SIZE, Int8TransTypeData::count, elog, ERROR, NULL, PG_GETARG_ARRAYTYPE_P, PG_GETARG_ARRAYTYPE_P_COPY, PG_GETARG_INT32, PG_RETURN_ARRAYTYPE_P, and Int8TransTypeData::sum.

{
    ArrayType  *transarray;
    int32       newval = PG_GETARG_INT32(1);
    Int8TransTypeData *transdata;

    /*
     * If we're invoked as an aggregate, we can cheat and modify our first
     * parameter in-place to reduce palloc overhead. Otherwise we need to make
     * a copy of it before scribbling on it.
     */
    if (AggCheckCallContext(fcinfo, NULL))
        transarray = PG_GETARG_ARRAYTYPE_P(0);
    else
        transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

    if (ARR_HASNULL(transarray) ||
        ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
        elog(ERROR, "expected 2-element int8 array");

    transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
    transdata->count++;
    transdata->sum += newval;

    PG_RETURN_ARRAYTYPE_P(transarray);
}

Datum int4_numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 2191 of file numeric.c.

References free_var(), init_var, int8_to_numericvar(), make_result(), PG_GETARG_INT32, PG_RETURN_NUMERIC, and val.

Referenced by gbt_numeric_penalty(), int4_accum(), and numeric_to_char().

{
    int32       val = PG_GETARG_INT32(0);
    Numeric     res;
    NumericVar  result;

    init_var(&result);

    int8_to_numericvar((int64) val, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum int4_sum ( PG_FUNCTION_ARGS   ) 

Definition at line 2885 of file numeric.c.

References AggCheckCallContext(), NULL, PG_ARGISNULL, PG_GETARG_INT32, PG_GETARG_INT64, PG_GETARG_POINTER, PG_RETURN_INT64, PG_RETURN_NULL, and PG_RETURN_POINTER.

{
    int64       newval;

    if (PG_ARGISNULL(0))
    {
        /* No non-null input seen so far... */
        if (PG_ARGISNULL(1))
            PG_RETURN_NULL();   /* still no non-null */
        /* This is the first non-null input. */
        newval = (int64) PG_GETARG_INT32(1);
        PG_RETURN_INT64(newval);
    }

    /*
     * If we're invoked as an aggregate, we can cheat and modify our first
     * parameter in-place to avoid palloc overhead. If not, we need to return
     * the new value of the transition variable. (If int8 is pass-by-value,
     * then of course this is useless as well as incorrect, so just ifdef it
     * out.)
     */
#ifndef USE_FLOAT8_BYVAL        /* controls int8 too */
    if (AggCheckCallContext(fcinfo, NULL))
    {
        int64      *oldsum = (int64 *) PG_GETARG_POINTER(0);

        /* Leave the running sum unchanged in the new input is null */
        if (!PG_ARGISNULL(1))
            *oldsum = *oldsum + (int64) PG_GETARG_INT32(1);

        PG_RETURN_POINTER(oldsum);
    }
    else
#endif
    {
        int64       oldsum = PG_GETARG_INT64(0);

        /* Leave sum unchanged if new input is null. */
        if (PG_ARGISNULL(1))
            PG_RETURN_INT64(oldsum);

        /* OK to do the addition. */
        newval = oldsum + (int64) PG_GETARG_INT32(1);

        PG_RETURN_INT64(newval);
    }
}

Datum int8_accum ( PG_FUNCTION_ARGS   ) 
Datum int8_avg ( PG_FUNCTION_ARGS   ) 

Definition at line 3037 of file numeric.c.

References ARR_DATA_PTR, ARR_HASNULL, ARR_OVERHEAD_NONULLS, ARR_SIZE, Int8TransTypeData::count, DirectFunctionCall1, DirectFunctionCall2, elog, ERROR, Int64GetDatumFast, int8_numeric(), numeric_div(), PG_GETARG_ARRAYTYPE_P, PG_RETURN_DATUM, PG_RETURN_NULL, and Int8TransTypeData::sum.

{
    ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
    Int8TransTypeData *transdata;
    Datum       countd,
                sumd;

    if (ARR_HASNULL(transarray) ||
        ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
        elog(ERROR, "expected 2-element int8 array");
    transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);

    /* SQL defines AVG of no values to be NULL */
    if (transdata->count == 0)
        PG_RETURN_NULL();

    countd = DirectFunctionCall1(int8_numeric,
                                 Int64GetDatumFast(transdata->count));
    sumd = DirectFunctionCall1(int8_numeric,
                               Int64GetDatumFast(transdata->sum));

    PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd));
}

Datum int8_avg_accum ( PG_FUNCTION_ARGS   ) 
Datum int8_numeric ( PG_FUNCTION_ARGS   ) 
Datum int8_sum ( PG_FUNCTION_ARGS   ) 

Definition at line 2934 of file numeric.c.

References DirectFunctionCall1, DirectFunctionCall2, int8_numeric(), numeric_add(), NumericGetDatum, PG_ARGISNULL, PG_GETARG_DATUM, PG_GETARG_NUMERIC, PG_RETURN_DATUM, PG_RETURN_NULL, and PG_RETURN_NUMERIC.

{
    Numeric     oldsum;
    Datum       newval;

    if (PG_ARGISNULL(0))
    {
        /* No non-null input seen so far... */
        if (PG_ARGISNULL(1))
            PG_RETURN_NULL();   /* still no non-null */
        /* This is the first non-null input. */
        newval = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1));
        PG_RETURN_DATUM(newval);
    }

    /*
     * Note that we cannot special-case the aggregate case here, as we do for
     * int2_sum and int4_sum: numeric is of variable size, so we cannot modify
     * our first parameter in-place.
     */

    oldsum = PG_GETARG_NUMERIC(0);

    /* Leave sum unchanged if new input is null. */
    if (PG_ARGISNULL(1))
        PG_RETURN_NUMERIC(oldsum);

    /* OK to do the addition. */
    newval = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1));

    PG_RETURN_DATUM(DirectFunctionCall2(numeric_add,
                                        NumericGetDatum(oldsum), newval));
}

static void int8_to_numericvar ( int64  val,
NumericVar var 
) [static]

Definition at line 3911 of file numeric.c.

References alloc_var(), DEC_DIGITS, NumericVar::digits, NumericVar::dscale, NumericVar::ndigits, NumericVar::sign, and NumericVar::weight.

Referenced by int2_numeric(), int4_numeric(), int8_numeric(), numeric_fac(), and width_bucket_numeric().

{
    uint64      uval,
                newuval;
    NumericDigit *ptr;
    int         ndigits;

    /* int8 can require at most 19 decimal digits; add one for safety */
    alloc_var(var, 20 / DEC_DIGITS);
    if (val < 0)
    {
        var->sign = NUMERIC_NEG;
        uval = -val;
    }
    else
    {
        var->sign = NUMERIC_POS;
        uval = val;
    }
    var->dscale = 0;
    if (val == 0)
    {
        var->ndigits = 0;
        var->weight = 0;
        return;
    }
    ptr = var->digits + var->ndigits;
    ndigits = 0;
    do
    {
        ptr--;
        ndigits++;
        newuval = uval / NBASE;
        *ptr = uval - newuval * NBASE;
        uval = newuval;
    } while (uval);
    var->digits = ptr;
    var->ndigits = ndigits;
    var->weight = ndigits - 1;
}

static void ln_var ( NumericVar arg,
NumericVar result,
int  rscale 
) [static]

Definition at line 5390 of file numeric.c.

References add_var(), cmp_var(), DEC_DIGITS, div_var_fast(), ereport, errcode(), errmsg(), ERROR, free_var(), init_var, mul_var(), NumericVar::ndigits, set_var_from_var(), sqrt_var(), sub_var(), and NumericVar::weight.

Referenced by log_var(), numeric_ln(), and power_var().

{
    NumericVar  x;
    NumericVar  xx;
    NumericVar  ni;
    NumericVar  elem;
    NumericVar  fact;
    int         local_rscale;
    int         cmp;

    cmp = cmp_var(arg, &const_zero);
    if (cmp == 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
                 errmsg("cannot take logarithm of zero")));
    else if (cmp < 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
                 errmsg("cannot take logarithm of a negative number")));

    local_rscale = rscale + 8;

    init_var(&x);
    init_var(&xx);
    init_var(&ni);
    init_var(&elem);
    init_var(&fact);

    set_var_from_var(arg, &x);
    set_var_from_var(&const_two, &fact);

    /* Reduce input into range 0.9 < x < 1.1 */
    while (cmp_var(&x, &const_zero_point_nine) <= 0)
    {
        local_rscale++;
        sqrt_var(&x, &x, local_rscale);
        mul_var(&fact, &const_two, &fact, 0);
    }
    while (cmp_var(&x, &const_one_point_one) >= 0)
    {
        local_rscale++;
        sqrt_var(&x, &x, local_rscale);
        mul_var(&fact, &const_two, &fact, 0);
    }

    /*
     * We use the Taylor series for 0.5 * ln((1+z)/(1-z)),
     *
     * z + z^3/3 + z^5/5 + ...
     *
     * where z = (x-1)/(x+1) is in the range (approximately) -0.053 .. 0.048
     * due to the above range-reduction of x.
     *
     * The convergence of this is not as fast as one would like, but is
     * tolerable given that z is small.
     */
    sub_var(&x, &const_one, result);
    add_var(&x, &const_one, &elem);
    div_var_fast(result, &elem, result, local_rscale, true);
    set_var_from_var(result, &xx);
    mul_var(result, result, &x, local_rscale);

    set_var_from_var(&const_one, &ni);

    for (;;)
    {
        add_var(&ni, &const_two, &ni);
        mul_var(&xx, &x, &xx, local_rscale);
        div_var_fast(&xx, &ni, &elem, local_rscale, true);

        if (elem.ndigits == 0)
            break;

        add_var(result, &elem, result);

        if (elem.weight < (result->weight - local_rscale * 2 / DEC_DIGITS))
            break;
    }

    /* Compensate for argument range reduction, round to requested rscale */
    mul_var(result, &fact, result, rscale);

    free_var(&x);
    free_var(&xx);
    free_var(&ni);
    free_var(&elem);
    free_var(&fact);
}

static void log_var ( NumericVar base,
NumericVar num,
NumericVar result 
) [static]

Definition at line 5488 of file numeric.c.

References DEC_DIGITS, div_var_fast(), NumericVar::dscale, free_var(), init_var, ln_var(), Max, Min, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, select_div_scale(), and NumericVar::weight.

Referenced by numeric_log().

{
    NumericVar  ln_base;
    NumericVar  ln_num;
    int         dec_digits;
    int         rscale;
    int         local_rscale;

    init_var(&ln_base);
    init_var(&ln_num);

    /* Set scale for ln() calculations --- compare numeric_ln() */

    /* Approx decimal digits before decimal point */
    dec_digits = (num->weight + 1) * DEC_DIGITS;

    if (dec_digits > 1)
        rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(dec_digits - 1);
    else if (dec_digits < 1)
        rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(1 - dec_digits);
    else
        rscale = NUMERIC_MIN_SIG_DIGITS;

    rscale = Max(rscale, base->dscale);
    rscale = Max(rscale, num->dscale);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

    local_rscale = rscale + 8;

    /* Form natural logarithms */
    ln_var(base, &ln_base, local_rscale);
    ln_var(num, &ln_num, local_rscale);

    ln_base.dscale = rscale;
    ln_num.dscale = rscale;

    /* Select scale for division result */
    rscale = select_div_scale(&ln_num, &ln_base);

    div_var_fast(&ln_num, &ln_base, result, rscale, true);

    free_var(&ln_num);
    free_var(&ln_base);
}

static Numeric make_result ( NumericVar var  )  [static]

Definition at line 3678 of file numeric.c.

References Assert, NumericData::choice, NumericVar::digits, NumericVar::dscale, dump_numeric, ereport, errcode(), errmsg(), ERROR, NumericChoice::n_header, NumericVar::ndigits, NUMERIC_CAN_BE_SHORT, NUMERIC_DIGITS, NUMERIC_DSCALE, NUMERIC_HDRSZ, NUMERIC_HDRSZ_SHORT, NUMERIC_NAN, NUMERIC_NDIGITS, NUMERIC_NEG, NUMERIC_SHORT, NUMERIC_SHORT_WEIGHT_SIGN_MASK, NUMERIC_WEIGHT, palloc(), SET_VARSIZE, NumericVar::sign, sign, and NumericVar::weight.

{
    Numeric     result;
    NumericDigit *digits = var->digits;
    int         weight = var->weight;
    int         sign = var->sign;
    int         n;
    Size        len;

    if (sign == NUMERIC_NAN)
    {
        result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT);

        SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT);
        result->choice.n_header = NUMERIC_NAN;
        /* the header word is all we need */

        dump_numeric("make_result()", result);
        return result;
    }

    n = var->ndigits;

    /* truncate leading zeroes */
    while (n > 0 && *digits == 0)
    {
        digits++;
        weight--;
        n--;
    }
    /* truncate trailing zeroes */
    while (n > 0 && digits[n - 1] == 0)
        n--;

    /* If zero result, force to weight=0 and positive sign */
    if (n == 0)
    {
        weight = 0;
        sign = NUMERIC_POS;
    }

    /* Build the result */
    if (NUMERIC_CAN_BE_SHORT(var->dscale, weight))
    {
        len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit);
        result = (Numeric) palloc(len);
        SET_VARSIZE(result, len);
        result->choice.n_short.n_header =
            (sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK)
             : NUMERIC_SHORT)
            | (var->dscale << NUMERIC_SHORT_DSCALE_SHIFT)
            | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0)
            | (weight & NUMERIC_SHORT_WEIGHT_MASK);
    }
    else
    {
        len = NUMERIC_HDRSZ + n * sizeof(NumericDigit);
        result = (Numeric) palloc(len);
        SET_VARSIZE(result, len);
        result->choice.n_long.n_sign_dscale =
            sign | (var->dscale & NUMERIC_DSCALE_MASK);
        result->choice.n_long.n_weight = weight;
    }

    memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit));
    Assert(NUMERIC_NDIGITS(result) == n);

    /* Check for overflow of int16 fields */
    if (NUMERIC_WEIGHT(result) != weight ||
        NUMERIC_DSCALE(result) != var->dscale)
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("value overflows numeric format")));

    dump_numeric("make_result()", result);
    return result;
}

static void mod_var ( NumericVar var1,
NumericVar var2,
NumericVar result 
) [static]

Definition at line 5085 of file numeric.c.

References div_var(), NumericVar::dscale, free_var(), init_var, mul_var(), and sub_var().

Referenced by numeric_mod().

{
    NumericVar  tmp;

    init_var(&tmp);

    /* ---------
     * We do this using the equation
     *      mod(x,y) = x - trunc(x/y)*y
     * div_var can be persuaded to give us trunc(x/y) directly.
     * ----------
     */
    div_var(var1, var2, &tmp, 0, false);

    mul_var(var2, &tmp, &tmp, var2->dscale);

    sub_var(var1, &tmp, result);

    free_var(&tmp);
}

static void mul_var ( NumericVar var1,
NumericVar var2,
NumericVar result,
int  rscale 
) [static]

Definition at line 4311 of file numeric.c.

References alloc_var(), Assert, NumericVar::digits, NumericVar::dscale, maxdigits, MUL_GUARD_DIGITS, NBASE, NumericVar::ndigits, palloc0(), pfree(), round_var(), NumericVar::sign, strip_var(), NumericVar::weight, and zero_var().

Referenced by compute_bucket(), exp_var(), exp_var_internal(), ln_var(), mod_var(), numeric_fac(), numeric_mul(), numeric_stddev_internal(), power_var(), power_var_int(), and sqrt_var().

{
    int         res_ndigits;
    int         res_sign;
    int         res_weight;
    int         maxdigits;
    int        *dig;
    int         carry;
    int         maxdig;
    int         newdig;
    NumericDigit *res_digits;
    int         i,
                ri,
                i1,
                i2;

    /* 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;

    if (var1ndigits == 0 || var2ndigits == 0)
    {
        /* one or both inputs is zero; so is result */
        zero_var(result);
        result->dscale = rscale;
        return;
    }

    /* Determine result sign and (maximum possible) weight */
    if (var1->sign == var2->sign)
        res_sign = NUMERIC_POS;
    else
        res_sign = NUMERIC_NEG;
    res_weight = var1->weight + var2->weight + 2;

    /*
     * Determine number of result digits to compute.  If the exact result
     * would have more than rscale fractional digits, truncate the computation
     * with MUL_GUARD_DIGITS guard digits.  We do that by pretending that one
     * or both inputs have fewer digits than they really do.
     */
    res_ndigits = var1ndigits + var2ndigits + 1;
    maxdigits = res_weight + 1 + (rscale * DEC_DIGITS) + MUL_GUARD_DIGITS;
    if (res_ndigits > maxdigits)
    {
        if (maxdigits < 3)
        {
            /* no useful precision at all in the result... */
            zero_var(result);
            result->dscale = rscale;
            return;
        }
        /* force maxdigits odd so that input ndigits can be equal */
        if ((maxdigits & 1) == 0)
            maxdigits++;
        if (var1ndigits > var2ndigits)
        {
            var1ndigits -= res_ndigits - maxdigits;
            if (var1ndigits < var2ndigits)
                var1ndigits = var2ndigits = (var1ndigits + var2ndigits) / 2;
        }
        else
        {
            var2ndigits -= res_ndigits - maxdigits;
            if (var2ndigits < var1ndigits)
                var1ndigits = var2ndigits = (var1ndigits + var2ndigits) / 2;
        }
        res_ndigits = maxdigits;
        Assert(res_ndigits == var1ndigits + var2ndigits + 1);
    }

    /*
     * We do the arithmetic in an array "dig[]" of signed int's.  Since
     * INT_MAX is noticeably larger than NBASE*NBASE, this gives us headroom
     * to avoid normalizing carries immediately.
     *
     * maxdig tracks the maximum possible value of any dig[] entry; when this
     * threatens to exceed INT_MAX, we take the time to propagate carries. To
     * avoid overflow in maxdig itself, it actually represents the max
     * possible value divided by NBASE-1.
     */
    dig = (int *) palloc0(res_ndigits * sizeof(int));
    maxdig = 0;

    ri = res_ndigits - 1;
    for (i1 = var1ndigits - 1; i1 >= 0; ri--, i1--)
    {
        int         var1digit = var1digits[i1];

        if (var1digit == 0)
            continue;

        /* Time to normalize? */
        maxdig += var1digit;
        if (maxdig > INT_MAX / (NBASE - 1))
        {
            /* Yes, do it */
            carry = 0;
            for (i = res_ndigits - 1; i >= 0; i--)
            {
                newdig = dig[i] + carry;
                if (newdig >= NBASE)
                {
                    carry = newdig / NBASE;
                    newdig -= carry * NBASE;
                }
                else
                    carry = 0;
                dig[i] = newdig;
            }
            Assert(carry == 0);
            /* Reset maxdig to indicate new worst-case */
            maxdig = 1 + var1digit;
        }

        /* Add appropriate multiple of var2 into the accumulator */
        i = ri;
        for (i2 = var2ndigits - 1; i2 >= 0; i2--)
            dig[i--] += var1digit * var2digits[i2];
    }

    /*
     * Now we do a final carry propagation pass to normalize the result, which
     * we combine with storing the result digits into the output. Note that
     * this is still done at full precision w/guard digits.
     */
    alloc_var(result, res_ndigits);
    res_digits = result->digits;
    carry = 0;
    for (i = res_ndigits - 1; i >= 0; i--)
    {
        newdig = dig[i] + carry;
        if (newdig >= NBASE)
        {
            carry = newdig / NBASE;
            newdig -= carry * NBASE;
        }
        else
            carry = 0;
        res_digits[i] = newdig;
    }
    Assert(carry == 0);

    pfree(dig);

    /*
     * Finally, round the result to the requested precision.
     */
    result->weight = res_weight;
    result->sign = res_sign;

    /* Round to target rscale (and set result->dscale) */
    round_var(result, rscale);

    /* Strip leading and trailing zeroes */
    strip_var(result);
}

Datum numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 768 of file numeric.c.

References apply_typmod(), NumericData::choice, DEC_DIGITS, free_var(), init_var, make_result(), maxdigits, NumericShort::n_header, NumericChoice::n_short, NUMERIC_CAN_BE_SHORT, NUMERIC_DSCALE, NUMERIC_DSCALE_MASK, NUMERIC_IS_NAN, NUMERIC_IS_SHORT, NUMERIC_SHORT_DSCALE_SHIFT, NUMERIC_SIGN, NUMERIC_WEIGHT, palloc(), PG_GETARG_INT32, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, scale, set_var_from_num(), VARHDRSZ, and VARSIZE.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    int32       typmod = PG_GETARG_INT32(1);
    Numeric     new;
    int32       tmp_typmod;
    int         precision;
    int         scale;
    int         ddigits;
    int         maxdigits;
    NumericVar  var;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * If the value isn't a valid type modifier, simply return a copy of the
     * input value
     */
    if (typmod < (int32) (VARHDRSZ))
    {
        new = (Numeric) palloc(VARSIZE(num));
        memcpy(new, num, VARSIZE(num));
        PG_RETURN_NUMERIC(new);
    }

    /*
     * Get the precision and scale out of the typmod value
     */
    tmp_typmod = typmod - VARHDRSZ;
    precision = (tmp_typmod >> 16) & 0xffff;
    scale = tmp_typmod & 0xffff;
    maxdigits = precision - scale;

    /*
     * If the number is certainly in bounds and due to the target scale no
     * rounding could be necessary, just make a copy of the input and modify
     * its scale fields, unless the larger scale forces us to abandon the
     * short representation.  (Note we assume the existing dscale is
     * honest...)
     */
    ddigits = (NUMERIC_WEIGHT(num) + 1) * DEC_DIGITS;
    if (ddigits <= maxdigits && scale >= NUMERIC_DSCALE(num)
        && (NUMERIC_CAN_BE_SHORT(scale, NUMERIC_WEIGHT(num))
            || !NUMERIC_IS_SHORT(num)))
    {
        new = (Numeric) palloc(VARSIZE(num));
        memcpy(new, num, VARSIZE(num));
        if (NUMERIC_IS_SHORT(num))
            new->choice.n_short.n_header =
                (num->choice.n_short.n_header & ~NUMERIC_SHORT_DSCALE_MASK)
                | (scale << NUMERIC_SHORT_DSCALE_SHIFT);
        else
            new->choice.n_long.n_sign_dscale = NUMERIC_SIGN(new) |
                ((uint16) scale & NUMERIC_DSCALE_MASK);
        PG_RETURN_NUMERIC(new);
    }

    /*
     * We really need to fiddle with things - unpack the number into a
     * variable and let apply_typmod() do it.
     */
    init_var(&var);

    set_var_from_num(num, &var);
    apply_typmod(&var, typmod);
    new = make_result(&var);

    free_var(&var);

    PG_RETURN_NUMERIC(new);
}

Datum numeric_abs ( PG_FUNCTION_ARGS   ) 

Definition at line 914 of file numeric.c.

References NumericData::choice, make_result(), NumericShort::n_header, NumericChoice::n_long, NumericChoice::n_short, NumericLong::n_sign_dscale, NUMERIC_DSCALE, NUMERIC_IS_NAN, NUMERIC_IS_SHORT, NUMERIC_POS, palloc(), PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and VARSIZE.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Do it the easy way directly on the packed format
     */
    res = (Numeric) palloc(VARSIZE(num));
    memcpy(res, num, VARSIZE(num));

    if (NUMERIC_IS_SHORT(num))
        res->choice.n_short.n_header =
            num->choice.n_short.n_header & ~NUMERIC_SHORT_SIGN_MASK;
    else
        res->choice.n_long.n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_accum ( PG_FUNCTION_ARGS   ) 
Datum numeric_add ( PG_FUNCTION_ARGS   ) 

Definition at line 1548 of file numeric.c.

References add_var(), free_var(), init_var, init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

Referenced by do_numeric_accum(), do_numeric_avg_accum(), int8_sum(), and numeric_plus_one_over_two().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the values, let add_var() compute the result and return it.
     */
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    init_var(&result);
    add_var(&arg1, &arg2, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_avg ( PG_FUNCTION_ARGS   ) 

Definition at line 2631 of file numeric.c.

References DatumGetNumeric, deconstruct_array(), DirectFunctionCall2, elog, ERROR, NULL, numeric_div(), NUMERIC_NDIGITS, NumericGetDatum, NUMERICOID, PG_GETARG_ARRAYTYPE_P, PG_RETURN_DATUM, and PG_RETURN_NULL.

{
    ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
    Datum      *transdatums;
    int         ndatums;
    Numeric     N,
                sumX;

    /* We assume the input is array of numeric */
    deconstruct_array(transarray,
                      NUMERICOID, -1, false, 'i',
                      &transdatums, NULL, &ndatums);
    if (ndatums != 2)
        elog(ERROR, "expected 2-element numeric array");
    N = DatumGetNumeric(transdatums[0]);
    sumX = DatumGetNumeric(transdatums[1]);

    /* SQL defines AVG of no values to be NULL */
    /* N is zero iff no digits (cf. numeric_uminus) */
    if (NUMERIC_NDIGITS(N) == 0)
        PG_RETURN_NULL();

    PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
                                        NumericGetDatum(sumX),
                                        NumericGetDatum(N)));
}

Datum numeric_avg_accum ( PG_FUNCTION_ARGS   ) 
Datum numeric_ceil ( PG_FUNCTION_ARGS   ) 

Definition at line 1142 of file numeric.c.

References ceil_var(), free_var(), init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;
    NumericVar  result;

    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    init_var_from_num(num, &result);
    ceil_var(&result, &result);

    res = make_result(&result);
    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_cmp ( PG_FUNCTION_ARGS   ) 

Definition at line 1320 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_INT32.

Referenced by gbt_numeric_cmp(), and gin_numeric_cmp().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    int         result;

    result = cmp_numerics(num1, num2);

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_INT32(result);
}

Datum numeric_div ( PG_FUNCTION_ARGS   ) 

Definition at line 1666 of file numeric.c.

References div_var(), free_var(), init_var, init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and select_div_scale().

Referenced by cash_numeric(), gbt_numeric_penalty(), int8_avg(), and numeric_avg().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;
    Numeric     res;
    int         rscale;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the arguments
     */
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    init_var(&result);

    /*
     * Select scale for division result
     */
    rscale = select_div_scale(&arg1, &arg2);

    /*
     * Do the divide and return the result
     */
    div_var(&arg1, &arg2, &result, rscale, true);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_div_trunc ( PG_FUNCTION_ARGS   ) 

Definition at line 1714 of file numeric.c.

References div_var(), free_var(), init_var, init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

Referenced by numeric_plus_one_over_two(), and numeric_shift_right().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the arguments
     */
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    init_var(&result);

    /*
     * Do the divide and return the result
     */
    div_var(&arg1, &arg2, &result, 0, false);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_eq ( PG_FUNCTION_ARGS   ) 

Definition at line 1336 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_BOOL.

Referenced by gbt_numeric_eq().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    bool        result;

    result = cmp_numerics(num1, num2) == 0;

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_BOOL(result);
}

Datum numeric_exp ( PG_FUNCTION_ARGS   ) 

Definition at line 1972 of file numeric.c.

References arg, NumericVar::dscale, exp_var(), free_var(), init_var, init_var_from_num(), make_result(), Max, Min, NUMERIC_IS_NAN, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MAX_RESULT_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, numericvar_to_double_no_overflow(), PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and val.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;
    NumericVar  arg;
    NumericVar  result;
    int         rscale;
    double      val;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the argument and determine the result scale.  We choose a scale
     * to give at least NUMERIC_MIN_SIG_DIGITS significant digits; but in any
     * case not less than the input's dscale.
     */
    init_var_from_num(num, &arg);

    init_var(&result);

    /* convert input to float8, ignoring overflow */
    val = numericvar_to_double_no_overflow(&arg);

    /*
     * log10(result) = num * log10(e), so this is approximately the decimal
     * weight of the result:
     */
    val *= 0.434294481903252;

    /* limit to something that won't cause integer overflow */
    val = Max(val, -NUMERIC_MAX_RESULT_SCALE);
    val = Min(val, NUMERIC_MAX_RESULT_SCALE);

    rscale = NUMERIC_MIN_SIG_DIGITS - (int) val;
    rscale = Max(rscale, arg.dscale);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

    /*
     * Let exp_var() do the calculation and return the result.
     */
    exp_var(&arg, &result, rscale);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_fac ( PG_FUNCTION_ARGS   ) 

Definition at line 1873 of file numeric.c.

References CHECK_FOR_INTERRUPTS, ereport, errcode(), errmsg(), ERROR, free_var(), init_var, int8_to_numericvar(), make_result(), mul_var(), PG_GETARG_INT64, and PG_RETURN_NUMERIC.

{
    int64       num = PG_GETARG_INT64(0);
    Numeric     res;
    NumericVar  fact;
    NumericVar  result;

    if (num <= 1)
    {
        res = make_result(&const_one);
        PG_RETURN_NUMERIC(res);
    }
    /* Fail immediately if the result would overflow */
    if (num > 32177)
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("value overflows numeric format")));

    init_var(&fact);
    init_var(&result);

    int8_to_numericvar(num, &result);

    for (num = num - 1; num > 1; num--)
    {
        /* this loop can take awhile, so allow it to be interrupted */
        CHECK_FOR_INTERRUPTS();

        int8_to_numericvar(num, &fact);

        mul_var(&result, &fact, &result, 0);
    }

    res = make_result(&result);

    free_var(&fact);
    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_float4 ( PG_FUNCTION_ARGS   ) 
Datum numeric_float8 ( PG_FUNCTION_ARGS   ) 
Datum numeric_float8_no_overflow ( PG_FUNCTION_ARGS   ) 
Datum numeric_floor ( PG_FUNCTION_ARGS   ) 

Definition at line 1167 of file numeric.c.

References floor_var(), free_var(), init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;
    NumericVar  result;

    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    init_var_from_num(num, &result);
    floor_var(&result, &result);

    res = make_result(&result);
    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_ge ( PG_FUNCTION_ARGS   ) 

Definition at line 1381 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_BOOL.

Referenced by gbt_numeric_ge().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    bool        result;

    result = cmp_numerics(num1, num2) >= 0;

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_BOOL(result);
}

Datum numeric_gt ( PG_FUNCTION_ARGS   ) 

Definition at line 1366 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_BOOL.

Referenced by gbt_numeric_gt(), and gbt_numeric_penalty().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    bool        result;

    result = cmp_numerics(num1, num2) > 0;

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_BOOL(result);
}

Datum numeric_in ( PG_FUNCTION_ARGS   ) 

Definition at line 451 of file numeric.c.

References apply_typmod(), ereport, errcode(), errmsg(), ERROR, free_var(), init_var, make_result(), PG_GETARG_CSTRING, PG_GETARG_INT32, PG_GETARG_OID, PG_RETURN_NUMERIC, pg_strncasecmp(), set_var_from_str(), and value.

Referenced by make_const(), numeric_to_number(), and ssl_client_serial().

{
    char       *str = PG_GETARG_CSTRING(0);

#ifdef NOT_USED
    Oid         typelem = PG_GETARG_OID(1);
#endif
    int32       typmod = PG_GETARG_INT32(2);
    Numeric     res;
    const char *cp;

    /* Skip leading spaces */
    cp = str;
    while (*cp)
    {
        if (!isspace((unsigned char) *cp))
            break;
        cp++;
    }

    /*
     * Check for NaN
     */
    if (pg_strncasecmp(cp, "NaN", 3) == 0)
    {
        res = make_result(&const_nan);

        /* Should be nothing left but spaces */
        cp += 3;
        while (*cp)
        {
            if (!isspace((unsigned char) *cp))
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                      errmsg("invalid input syntax for type numeric: \"%s\"",
                             str)));
            cp++;
        }
    }
    else
    {
        /*
         * Use set_var_from_str() to parse a normal numeric value
         */
        NumericVar  value;

        init_var(&value);

        cp = set_var_from_str(str, cp, &value);

        /*
         * We duplicate a few lines of code here because we would like to
         * throw any trailing-junk syntax error before any semantic error
         * resulting from apply_typmod.  We can't easily fold the two cases
         * together because we mustn't apply apply_typmod to a NaN.
         */
        while (*cp)
        {
            if (!isspace((unsigned char) *cp))
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                      errmsg("invalid input syntax for type numeric: \"%s\"",
                             str)));
            cp++;
        }

        apply_typmod(&value, typmod);

        res = make_result(&value);
        free_var(&value);
    }

    PG_RETURN_NUMERIC(res);
}

Datum numeric_inc ( PG_FUNCTION_ARGS   ) 

Definition at line 1789 of file numeric.c.

References add_var(), arg, free_var(), init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

Referenced by do_numeric_accum(), and do_numeric_avg_accum().

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    NumericVar  arg;
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Compute the result and return it
     */
    init_var_from_num(num, &arg);

    add_var(&arg, &const_one, &arg);

    res = make_result(&arg);

    free_var(&arg);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_int2 ( PG_FUNCTION_ARGS   ) 

Definition at line 2320 of file numeric.c.

References ereport, errcode(), errmsg(), ERROR, init_var_from_num(), NUMERIC_IS_NAN, numericvar_to_int8(), PG_GETARG_NUMERIC, PG_RETURN_INT16, and val.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    NumericVar  x;
    int64       val;
    int16       result;

    /* XXX would it be better to return NULL? */
    if (NUMERIC_IS_NAN(num))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("cannot convert NaN to smallint")));

    /* Convert to variable format and thence to int8 */
    init_var_from_num(num, &x);

    if (!numericvar_to_int8(&x, &val))
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("smallint out of range")));

    /* Down-convert to int2 */
    result = (int16) val;

    /* Test for overflow by reverse-conversion. */
    if ((int64) result != val)
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("smallint out of range")));

    PG_RETURN_INT16(result);
}

Datum numeric_int4 ( PG_FUNCTION_ARGS   ) 

Definition at line 2210 of file numeric.c.

References ereport, errcode(), errmsg(), ERROR, init_var_from_num(), NUMERIC_IS_NAN, numericvar_to_int4(), PG_GETARG_NUMERIC, and PG_RETURN_INT32.

Referenced by numeric_to_char().

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    NumericVar  x;
    int32       result;

    /* XXX would it be better to return NULL? */
    if (NUMERIC_IS_NAN(num))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("cannot convert NaN to integer")));

    /* Convert to variable format, then convert to int4 */
    init_var_from_num(num, &x);
    result = numericvar_to_int4(&x);
    PG_RETURN_INT32(result);
}

Datum numeric_int8 ( PG_FUNCTION_ARGS   ) 

Definition at line 2276 of file numeric.c.

References ereport, errcode(), errmsg(), ERROR, init_var_from_num(), NUMERIC_IS_NAN, numericvar_to_int8(), PG_GETARG_NUMERIC, and PG_RETURN_INT64.

Referenced by numeric_cash().

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    NumericVar  x;
    int64       result;

    /* XXX would it be better to return NULL? */
    if (NUMERIC_IS_NAN(num))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("cannot convert NaN to bigint")));

    /* Convert to variable format and thence to int8 */
    init_var_from_num(num, &x);

    if (!numericvar_to_int8(&x, &result))
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("bigint out of range")));

    PG_RETURN_INT64(result);
}

bool numeric_is_nan ( Numeric  num  ) 

Definition at line 561 of file numeric.c.

References NUMERIC_IS_NAN.

Referenced by gbt_numeric_penalty().

{
    return NUMERIC_IS_NAN(num);
}

Datum numeric_larger ( PG_FUNCTION_ARGS   ) 

Definition at line 1844 of file numeric.c.

References cmp_numerics(), PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);

    /*
     * Use cmp_numerics so that this will agree with the comparison operators,
     * particularly as regards comparisons involving NaN.
     */
    if (cmp_numerics(num1, num2) > 0)
        PG_RETURN_NUMERIC(num1);
    else
        PG_RETURN_NUMERIC(num2);
}

Datum numeric_le ( PG_FUNCTION_ARGS   ) 

Definition at line 1411 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_BOOL.

Referenced by gbt_numeric_le().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    bool        result;

    result = cmp_numerics(num1, num2) <= 0;

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_BOOL(result);
}

Datum numeric_ln ( PG_FUNCTION_ARGS   ) 

Definition at line 2033 of file numeric.c.

References arg, DEC_DIGITS, NumericVar::dscale, free_var(), init_var, init_var_from_num(), ln_var(), make_result(), Max, Min, NUMERIC_IS_NAN, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and NumericVar::weight.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;
    NumericVar  arg;
    NumericVar  result;
    int         dec_digits;
    int         rscale;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    init_var_from_num(num, &arg);
    init_var(&result);

    /* Approx decimal digits before decimal point */
    dec_digits = (arg.weight + 1) * DEC_DIGITS;

    if (dec_digits > 1)
        rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(dec_digits - 1);
    else if (dec_digits < 1)
        rscale = NUMERIC_MIN_SIG_DIGITS - (int) log10(1 - dec_digits);
    else
        rscale = NUMERIC_MIN_SIG_DIGITS;

    rscale = Max(rscale, arg.dscale);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

    ln_var(&arg, &result, rscale);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_log ( PG_FUNCTION_ARGS   ) 

Definition at line 2081 of file numeric.c.

References free_var(), init_var, init_var_from_num(), log_var(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    Numeric     res;
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Initialize things
     */
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);
    init_var(&result);

    /*
     * Call log_var() to compute and return the result; note it handles scale
     * selection itself.
     */
    log_var(&arg1, &arg2, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_lt ( PG_FUNCTION_ARGS   ) 

Definition at line 1396 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_BOOL.

Referenced by gbt_numeric_lt(), and numeric_is_less().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    bool        result;

    result = cmp_numerics(num1, num2) < 0;

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_BOOL(result);
}

int32 numeric_maximum_size ( int32  typmod  ) 

Definition at line 572 of file numeric.c.

References DEC_DIGITS, NUMERIC_HDRSZ, and VARHDRSZ.

Referenced by type_maximum_size().

{
    int         precision;
    int         numeric_digits;

    if (typmod < (int32) (VARHDRSZ))
        return -1;

    /* precision (ie, max # of digits) is in upper bits of typmod */
    precision = ((typmod - VARHDRSZ) >> 16) & 0xffff;

    /*
     * This formula computes the maximum number of NumericDigits we could need
     * in order to store the specified number of decimal digits. Because the
     * weight is stored as a number of NumericDigits rather than a number of
     * decimal digits, it's possible that the first NumericDigit will contain
     * only a single decimal digit.  Thus, the first two decimal digits can
     * require two NumericDigits to store, but it isn't until we reach
     * DEC_DIGITS + 2 decimal digits that we potentially need a third
     * NumericDigit.
     */
    numeric_digits = (precision + 2 * (DEC_DIGITS - 1)) / DEC_DIGITS;

    /*
     * In most cases, the size of a numeric will be smaller than the value
     * computed below, because the varlena header will typically get toasted
     * down to a single byte before being stored on disk, and it may also be
     * possible to use a short numeric header.  But our job here is to compute
     * the worst case.
     */
    return NUMERIC_HDRSZ + (numeric_digits * sizeof(NumericDigit));
}

Datum numeric_mod ( PG_FUNCTION_ARGS   ) 

Definition at line 1756 of file numeric.c.

References free_var(), init_var, init_var_from_num(), make_result(), mod_var(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    Numeric     res;
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;

    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    init_var(&result);

    mod_var(&arg1, &arg2, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_mul ( PG_FUNCTION_ARGS   ) 

Definition at line 1624 of file numeric.c.

References NumericVar::dscale, free_var(), init_var, init_var_from_num(), make_result(), mul_var(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

Referenced by do_numeric_accum(), numeric_cash(), and numeric_to_char().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the values, let mul_var() compute the result and return it.
     * Unlike add_var() and sub_var(), mul_var() will round its result. In the
     * case of numeric_mul(), which is invoked for the * operator on numerics,
     * we request exact representation for the product (rscale = sum(dscale of
     * arg1, dscale of arg2)).
     */
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    init_var(&result);
    mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_ne ( PG_FUNCTION_ARGS   ) 

Definition at line 1351 of file numeric.c.

References cmp_numerics(), PG_FREE_IF_COPY, PG_GETARG_NUMERIC, and PG_RETURN_BOOL.

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    bool        result;

    result = cmp_numerics(num1, num2) != 0;

    PG_FREE_IF_COPY(num1, 0);
    PG_FREE_IF_COPY(num2, 1);

    PG_RETURN_BOOL(result);
}

Datum numeric_out ( PG_FUNCTION_ARGS   ) 

Definition at line 533 of file numeric.c.

References get_str_from_var(), init_var_from_num(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, PG_RETURN_CSTRING, and pstrdup().

Referenced by numeric_float4(), numeric_float8(), numeric_to_char(), numeric_to_cstring(), and numeric_to_double_no_overflow().

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    NumericVar  x;
    char       *str;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_CSTRING(pstrdup("NaN"));

    /*
     * Get the number in the variable format.
     */
    init_var_from_num(num, &x);

    str = get_str_from_var(&x);

    PG_RETURN_CSTRING(str);
}

char* numeric_out_sci ( Numeric  num,
int  scale 
)

Definition at line 611 of file numeric.c.

References get_str_from_var_sci(), init_var_from_num(), NUMERIC_IS_NAN, and pstrdup().

Referenced by int8_to_char(), and numeric_to_char().

{
    NumericVar  x;
    char       *str;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        return pstrdup("NaN");

    init_var_from_num(num, &x);

    str = get_str_from_var_sci(&x, scale);

    return str;
}

Datum numeric_power ( PG_FUNCTION_ARGS   ) 

Definition at line 2123 of file numeric.c.

References cmp_var(), ereport, errcode(), errmsg(), ERROR, free_var(), init_var, init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, power_var(), set_var_from_var(), and trunc_var().

Referenced by numeric_to_char().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    Numeric     res;
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  arg2_trunc;
    NumericVar  result;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Initialize things
     */
    init_var(&arg2_trunc);
    init_var(&result);
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    set_var_from_var(&arg2, &arg2_trunc);
    trunc_var(&arg2_trunc, 0);

    /*
     * The SQL spec requires that we emit a particular SQLSTATE error code for
     * certain error conditions.  Specifically, we don't return a
     * divide-by-zero error code for 0 ^ -1.
     */
    if (cmp_var(&arg1, &const_zero) == 0 &&
        cmp_var(&arg2, &const_zero) < 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
                 errmsg("zero raised to a negative power is undefined")));

    if (cmp_var(&arg1, &const_zero) < 0 &&
        cmp_var(&arg2, &arg2_trunc) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
                 errmsg("a negative number raised to a non-integer power yields a complex result")));

    /*
     * Call power_var() to compute and return the result; note it handles
     * scale selection itself.
     */
    power_var(&arg1, &arg2, &result);

    res = make_result(&result);

    free_var(&result);
    free_var(&arg2_trunc);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_recv ( PG_FUNCTION_ARGS   ) 

Definition at line 636 of file numeric.c.

References alloc_var(), apply_typmod(), buf, NumericVar::digits, NumericVar::dscale, ereport, errcode(), errmsg(), ERROR, free_var(), i, init_var, make_result(), NBASE, NUMERIC_MAX_PRECISION, NUMERIC_MAX_RESULT_SCALE, NUMERIC_NAN, NUMERIC_NEG, NUMERIC_POS, PG_GETARG_INT32, PG_GETARG_OID, PG_GETARG_POINTER, PG_RETURN_NUMERIC, pq_getmsgint(), NumericVar::sign, value, and NumericVar::weight.

{
    StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
    Oid         typelem = PG_GETARG_OID(1);
#endif
    int32       typmod = PG_GETARG_INT32(2);
    NumericVar  value;
    Numeric     res;
    int         len,
                i;

    init_var(&value);

    len = (uint16) pq_getmsgint(buf, sizeof(uint16));
    if (len < 0 || len > NUMERIC_MAX_PRECISION + NUMERIC_MAX_RESULT_SCALE)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
                 errmsg("invalid length in external \"numeric\" value")));

    alloc_var(&value, len);

    value.weight = (int16) pq_getmsgint(buf, sizeof(int16));
    value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16));
    if (!(value.sign == NUMERIC_POS ||
          value.sign == NUMERIC_NEG ||
          value.sign == NUMERIC_NAN))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
                 errmsg("invalid sign in external \"numeric\" value")));

    value.dscale = (uint16) pq_getmsgint(buf, sizeof(uint16));
    for (i = 0; i < len; i++)
    {
        NumericDigit d = pq_getmsgint(buf, sizeof(NumericDigit));

        if (d < 0 || d >= NBASE)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
                     errmsg("invalid digit in external \"numeric\" value")));
        value.digits[i] = d;
    }

    apply_typmod(&value, typmod);

    res = make_result(&value);
    free_var(&value);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_round ( PG_FUNCTION_ARGS   ) 

Definition at line 1046 of file numeric.c.

References arg, NumericVar::dscale, free_var(), init_var, make_result(), Max, Min, NUMERIC_IS_NAN, NUMERIC_MAX_RESULT_SCALE, PG_GETARG_INT32, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, round_var(), scale, and set_var_from_num().

Referenced by cash_numeric(), and numeric_to_char().

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    int32       scale = PG_GETARG_INT32(1);
    Numeric     res;
    NumericVar  arg;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Limit the scale value to avoid possible overflow in calculations
     */
    scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE);
    scale = Min(scale, NUMERIC_MAX_RESULT_SCALE);

    /*
     * Unpack the argument and round it at the proper digit position
     */
    init_var(&arg);
    set_var_from_num(num, &arg);

    round_var(&arg, scale);

    /* We don't allow negative output dscale */
    if (scale < 0)
        arg.dscale = 0;

    /*
     * Return the rounded result
     */
    res = make_result(&arg);

    free_var(&arg);
    PG_RETURN_NUMERIC(res);
}

Datum numeric_send ( PG_FUNCTION_ARGS   ) 
Datum numeric_sign ( PG_FUNCTION_ARGS   ) 

Definition at line 1001 of file numeric.c.

References free_var(), init_var, make_result(), NUMERIC_IS_NAN, NUMERIC_NDIGITS, NUMERIC_SIGN, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, set_var_from_var(), and NumericVar::sign.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;
    NumericVar  result;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    init_var(&result);

    /*
     * The packed format is known to be totally zero digit trimmed always. So
     * we can identify a ZERO by the fact that there are no digits at all.
     */
    if (NUMERIC_NDIGITS(num) == 0)
        set_var_from_var(&const_zero, &result);
    else
    {
        /*
         * And if there are some, we return a copy of ONE with the sign of our
         * argument
         */
        set_var_from_var(&const_one, &result);
        result.sign = NUMERIC_SIGN(num);
    }

    res = make_result(&result);
    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

Datum numeric_smaller ( PG_FUNCTION_ARGS   ) 

Definition at line 1822 of file numeric.c.

References cmp_numerics(), PG_GETARG_NUMERIC, and PG_RETURN_NUMERIC.

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);

    /*
     * Use cmp_numerics so that this will agree with the comparison operators,
     * particularly as regards comparisons involving NaN.
     */
    if (cmp_numerics(num1, num2) < 0)
        PG_RETURN_NUMERIC(num1);
    else
        PG_RETURN_NUMERIC(num2);
}

Datum numeric_sqrt ( PG_FUNCTION_ARGS   ) 

Definition at line 1921 of file numeric.c.

References arg, DEC_DIGITS, NumericVar::dscale, free_var(), init_var, init_var_from_num(), make_result(), Max, Min, NUMERIC_IS_NAN, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, sqrt_var(), and NumericVar::weight.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;
    NumericVar  arg;
    NumericVar  result;
    int         sweight;
    int         rscale;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the argument and determine the result scale.  We choose a scale
     * to give at least NUMERIC_MIN_SIG_DIGITS significant digits; but in any
     * case not less than the input's dscale.
     */
    init_var_from_num(num, &arg);

    init_var(&result);

    /* Assume the input was normalized, so arg.weight is accurate */
    sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1;

    rscale = NUMERIC_MIN_SIG_DIGITS - sweight;
    rscale = Max(rscale, arg.dscale);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

    /*
     * Let sqrt_var() do the calculation and return the result.
     */
    sqrt_var(&arg, &result, rscale);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

static Numeric numeric_stddev_internal ( ArrayType transarray,
bool  variance,
bool  sample,
bool is_null 
) [static]

Definition at line 2670 of file numeric.c.

References cmp_var(), DatumGetNumeric, deconstruct_array(), div_var(), NumericVar::dscale, elog, ERROR, free_var(), init_var, init_var_from_num(), make_result(), mul_var(), NULL, NUMERIC_IS_NAN, NUMERICOID, select_div_scale(), sqrt_var(), and sub_var().

Referenced by numeric_stddev_pop(), numeric_stddev_samp(), numeric_var_pop(), and numeric_var_samp().

{
    Datum      *transdatums;
    int         ndatums;
    Numeric     N,
                sumX,
                sumX2,
                res;
    NumericVar  vN,
                vsumX,
                vsumX2,
                vNminus1;
    NumericVar *comp;
    int         rscale;

    *is_null = false;

    /* We assume the input is array of numeric */
    deconstruct_array(transarray,
                      NUMERICOID, -1, false, 'i',
                      &transdatums, NULL, &ndatums);
    if (ndatums != 3)
        elog(ERROR, "expected 3-element numeric array");
    N = DatumGetNumeric(transdatums[0]);
    sumX = DatumGetNumeric(transdatums[1]);
    sumX2 = DatumGetNumeric(transdatums[2]);

    if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
        return make_result(&const_nan);

    init_var_from_num(N, &vN);

    /*
     * Sample stddev and variance are undefined when N <= 1; population stddev
     * is undefined when N == 0. Return NULL in either case.
     */
    if (sample)
        comp = &const_one;
    else
        comp = &const_zero;

    if (cmp_var(&vN, comp) <= 0)
    {
        *is_null = true;
        return NULL;
    }

    init_var(&vNminus1);
    sub_var(&vN, &const_one, &vNminus1);

    init_var_from_num(sumX, &vsumX);
    init_var_from_num(sumX2, &vsumX2);

    /* compute rscale for mul_var calls */
    rscale = vsumX.dscale * 2;

    mul_var(&vsumX, &vsumX, &vsumX, rscale);    /* vsumX = sumX * sumX */
    mul_var(&vN, &vsumX2, &vsumX2, rscale);     /* vsumX2 = N * sumX2 */
    sub_var(&vsumX2, &vsumX, &vsumX2);  /* N * sumX2 - sumX * sumX */

    if (cmp_var(&vsumX2, &const_zero) <= 0)
    {
        /* Watch out for roundoff error producing a negative numerator */
        res = make_result(&const_zero);
    }
    else
    {
        if (sample)
            mul_var(&vN, &vNminus1, &vNminus1, 0);      /* N * (N - 1) */
        else
            mul_var(&vN, &vN, &vNminus1, 0);    /* N * N */
        rscale = select_div_scale(&vsumX2, &vNminus1);
        div_var(&vsumX2, &vNminus1, &vsumX, rscale, true);      /* variance */
        if (!variance)
            sqrt_var(&vsumX, &vsumX, rscale);   /* stddev */

        res = make_result(&vsumX);
    }

    free_var(&vNminus1);
    free_var(&vsumX);
    free_var(&vsumX2);

    return res;
}

Datum numeric_stddev_pop ( PG_FUNCTION_ARGS   ) 

Definition at line 2804 of file numeric.c.

References numeric_stddev_internal(), PG_GETARG_ARRAYTYPE_P, PG_RETURN_NULL, and PG_RETURN_NUMERIC.

{
    Numeric     res;
    bool        is_null;

    res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
                                  false, false, &is_null);

    if (is_null)
        PG_RETURN_NULL();
    else
        PG_RETURN_NUMERIC(res);
}

Datum numeric_stddev_samp ( PG_FUNCTION_ARGS   ) 

Definition at line 2774 of file numeric.c.

References numeric_stddev_internal(), PG_GETARG_ARRAYTYPE_P, PG_RETURN_NULL, and PG_RETURN_NUMERIC.

{
    Numeric     res;
    bool        is_null;

    res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
                                  false, true, &is_null);

    if (is_null)
        PG_RETURN_NULL();
    else
        PG_RETURN_NUMERIC(res);
}

Datum numeric_sub ( PG_FUNCTION_ARGS   ) 

Definition at line 1586 of file numeric.c.

References free_var(), init_var, init_var_from_num(), make_result(), NUMERIC_IS_NAN, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and sub_var().

Referenced by gbt_numeric_penalty(), numrange_subdiff(), and pg_xlog_location_diff().

{
    Numeric     num1 = PG_GETARG_NUMERIC(0);
    Numeric     num2 = PG_GETARG_NUMERIC(1);
    NumericVar  arg1;
    NumericVar  arg2;
    NumericVar  result;
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Unpack the values, let sub_var() compute the result and return it.
     */
    init_var_from_num(num1, &arg1);
    init_var_from_num(num2, &arg2);

    init_var(&result);
    sub_var(&arg1, &arg2, &result);

    res = make_result(&result);

    free_var(&result);

    PG_RETURN_NUMERIC(res);
}

static double numeric_to_double_no_overflow ( Numeric  num  )  [static]

Definition at line 3956 of file numeric.c.

References DatumGetCString, DirectFunctionCall1, ereport, errcode(), errmsg(), ERROR, numeric_out(), NumericGetDatum, pfree(), and val.

Referenced by numeric_float8_no_overflow().

{
    char       *tmp;
    double      val;
    char       *endptr;

    tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
                                              NumericGetDatum(num)));

    /* unlike float8in, we ignore ERANGE from strtod */
    val = strtod(tmp, &endptr);
    if (*endptr != '\0')
    {
        /* shouldn't happen ... */
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
             errmsg("invalid input syntax for type double precision: \"%s\"",
                    tmp)));
    }

    pfree(tmp);

    return val;
}

Datum numeric_transform ( PG_FUNCTION_ARGS   ) 

Definition at line 724 of file numeric.c.

References FuncExpr::args, Assert, DatumGetInt32, exprTypmod(), IsA, linitial, list_length(), lsecond, PG_GETARG_POINTER, PG_RETURN_POINTER, relabel_to_typmod(), and VARHDRSZ.

{
    FuncExpr   *expr = (FuncExpr *) PG_GETARG_POINTER(0);
    Node       *ret = NULL;
    Node       *typmod;

    Assert(IsA(expr, FuncExpr));
    Assert(list_length(expr->args) >= 2);

    typmod = (Node *) lsecond(expr->args);

    if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
    {
        Node       *source = (Node *) linitial(expr->args);
        int32       old_typmod = exprTypmod(source);
        int32       new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
        int32       old_scale = (old_typmod - VARHDRSZ) & 0xffff;
        int32       new_scale = (new_typmod - VARHDRSZ) & 0xffff;
        int32       old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff;
        int32       new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff;

        /*
         * If new_typmod < VARHDRSZ, the destination is unconstrained; that's
         * always OK.  If old_typmod >= VARHDRSZ, the source is constrained,
         * and we're OK if the scale is unchanged and the precision is not
         * decreasing.  See further notes in function header comment.
         */
        if (new_typmod < (int32) VARHDRSZ ||
            (old_typmod >= (int32) VARHDRSZ &&
             new_scale == old_scale && new_precision >= old_precision))
            ret = relabel_to_typmod(source, new_typmod);
    }

    PG_RETURN_POINTER(ret);
}

Datum numeric_trunc ( PG_FUNCTION_ARGS   ) 

Definition at line 1095 of file numeric.c.

References arg, NumericVar::dscale, free_var(), init_var, make_result(), Max, Min, NUMERIC_IS_NAN, NUMERIC_MAX_RESULT_SCALE, PG_GETARG_INT32, PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, scale, set_var_from_num(), and trunc_var().

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    int32       scale = PG_GETARG_INT32(1);
    Numeric     res;
    NumericVar  arg;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Limit the scale value to avoid possible overflow in calculations
     */
    scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE);
    scale = Min(scale, NUMERIC_MAX_RESULT_SCALE);

    /*
     * Unpack the argument and truncate it at the proper digit position
     */
    init_var(&arg);
    set_var_from_num(num, &arg);

    trunc_var(&arg, scale);

    /* We don't allow negative output dscale */
    if (scale < 0)
        arg.dscale = 0;

    /*
     * Return the truncated result
     */
    res = make_result(&arg);

    free_var(&arg);
    PG_RETURN_NUMERIC(res);
}

Datum numeric_uminus ( PG_FUNCTION_ARGS   ) 

Definition at line 942 of file numeric.c.

References NumericData::choice, make_result(), NumericShort::n_header, NumericChoice::n_long, NumericChoice::n_short, NumericLong::n_sign_dscale, NUMERIC_DSCALE, NUMERIC_IS_NAN, NUMERIC_IS_SHORT, NUMERIC_NDIGITS, NUMERIC_NEG, NUMERIC_POS, NUMERIC_SIGN, palloc(), PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and VARSIZE.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;

    /*
     * Handle NaN
     */
    if (NUMERIC_IS_NAN(num))
        PG_RETURN_NUMERIC(make_result(&const_nan));

    /*
     * Do it the easy way directly on the packed format
     */
    res = (Numeric) palloc(VARSIZE(num));
    memcpy(res, num, VARSIZE(num));

    /*
     * The packed format is known to be totally zero digit trimmed always. So
     * we can identify a ZERO by the fact that there are no digits at all.  Do
     * nothing to a zero.
     */
    if (NUMERIC_NDIGITS(num) != 0)
    {
        /* Else, flip the sign */
        if (NUMERIC_IS_SHORT(num))
            res->choice.n_short.n_header =
                num->choice.n_short.n_header ^ NUMERIC_SHORT_SIGN_MASK;
        else if (NUMERIC_SIGN(num) == NUMERIC_POS)
            res->choice.n_long.n_sign_dscale =
                NUMERIC_NEG | NUMERIC_DSCALE(num);
        else
            res->choice.n_long.n_sign_dscale =
                NUMERIC_POS | NUMERIC_DSCALE(num);
    }

    PG_RETURN_NUMERIC(res);
}

Datum numeric_uplus ( PG_FUNCTION_ARGS   ) 

Definition at line 983 of file numeric.c.

References palloc(), PG_GETARG_NUMERIC, PG_RETURN_NUMERIC, and VARSIZE.

{
    Numeric     num = PG_GETARG_NUMERIC(0);
    Numeric     res;

    res = (Numeric) palloc(VARSIZE(num));
    memcpy(res, num, VARSIZE(num));

    PG_RETURN_NUMERIC(res);
}

Datum numeric_var_pop ( PG_FUNCTION_ARGS   ) 

Definition at line 2789 of file numeric.c.

References numeric_stddev_internal(), PG_GETARG_ARRAYTYPE_P, PG_RETURN_NULL, and PG_RETURN_NUMERIC.

{
    Numeric     res;
    bool        is_null;

    res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
                                  true, false, &is_null);

    if (is_null)
        PG_RETURN_NULL();
    else
        PG_RETURN_NUMERIC(res);
}

Datum numeric_var_samp ( PG_FUNCTION_ARGS   ) 

Definition at line 2759 of file numeric.c.

References numeric_stddev_internal(), PG_GETARG_ARRAYTYPE_P, PG_RETURN_NULL, and PG_RETURN_NUMERIC.

{
    Numeric     res;
    bool        is_null;

    res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
                                  true, true, &is_null);

    if (is_null)
        PG_RETURN_NULL();
    else
        PG_RETURN_NUMERIC(res);
}

Datum numerictypmodin ( PG_FUNCTION_ARGS   ) 

Definition at line 845 of file numeric.c.

References ArrayGetIntegerTypmods(), ereport, errcode(), errmsg(), ERROR, NUMERIC_MAX_PRECISION, PG_GETARG_ARRAYTYPE_P, PG_RETURN_INT32, and VARHDRSZ.

{
    ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
    int32      *tl;
    int         n;
    int32       typmod;

    tl = ArrayGetIntegerTypmods(ta, &n);

    if (n == 2)
    {
        if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("NUMERIC precision %d must be between 1 and %d",
                            tl[0], NUMERIC_MAX_PRECISION)));
        if (tl[1] < 0 || tl[1] > tl[0])
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("NUMERIC scale %d must be between 0 and precision %d",
                       tl[1], tl[0])));
        typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ;
    }
    else if (n == 1)
    {
        if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("NUMERIC precision %d must be between 1 and %d",
                            tl[0], NUMERIC_MAX_PRECISION)));
        /* scale defaults to zero */
        typmod = (tl[0] << 16) + VARHDRSZ;
    }
    else
    {
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid NUMERIC type modifier")));
        typmod = 0;             /* keep compiler quiet */
    }

    PG_RETURN_INT32(typmod);
}

Datum numerictypmodout ( PG_FUNCTION_ARGS   ) 

Definition at line 890 of file numeric.c.

References palloc(), PG_GETARG_INT32, PG_RETURN_CSTRING, snprintf(), and VARHDRSZ.

{
    int32       typmod = PG_GETARG_INT32(0);
    char       *res = (char *) palloc(64);

    if (typmod >= 0)
        snprintf(res, 64, "(%d,%d)",
                 ((typmod - VARHDRSZ) >> 16) & 0xffff,
                 (typmod - VARHDRSZ) & 0xffff);
    else
        *res = '\0';

    PG_RETURN_CSTRING(res);
}

static double numericvar_to_double_no_overflow ( NumericVar var  )  [static]

Definition at line 3983 of file numeric.c.

References ereport, errcode(), errmsg(), ERROR, get_str_from_var(), pfree(), and val.

Referenced by numeric_exp(), and power_var().

{
    char       *tmp;
    double      val;
    char       *endptr;

    tmp = get_str_from_var(var);

    /* unlike float8in, we ignore ERANGE from strtod */
    val = strtod(tmp, &endptr);
    if (*endptr != '\0')
    {
        /* shouldn't happen ... */
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
             errmsg("invalid input syntax for type double precision: \"%s\"",
                    tmp)));
    }

    pfree(tmp);

    return val;
}

static int32 numericvar_to_int4 ( NumericVar var  )  [static]

Definition at line 2234 of file numeric.c.

References ereport, errcode(), errmsg(), ERROR, numericvar_to_int8(), and val.

Referenced by numeric_int4(), and width_bucket_numeric().

{
    int32       result;
    int64       val;

    if (!numericvar_to_int8(var, &val))
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("integer out of range")));

    /* Down-convert to int4 */
    result = (int32) val;

    /* Test for overflow by reverse-conversion. */
    if ((int64) result != val)
        ereport(ERROR,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("integer out of range")));

    return result;
}

static bool numericvar_to_int8 ( NumericVar var,
int64 *  result 
) [static]

Definition at line 3840 of file numeric.c.

References Assert, NumericVar::digits, free_var(), init_var, NBASE, NumericVar::ndigits, round_var(), set_var_from_var(), NumericVar::sign, strip_var(), val, and NumericVar::weight.

Referenced by numeric_int2(), numeric_int8(), numericvar_to_int4(), and power_var().

{
    NumericDigit *digits;
    int         ndigits;
    int         weight;
    int         i;
    int64       val,
                oldval;
    bool        neg;
    NumericVar  rounded;

    /* Round to nearest integer */
    init_var(&rounded);
    set_var_from_var(var, &rounded);
    round_var(&rounded, 0);

    /* Check for zero input */
    strip_var(&rounded);
    ndigits = rounded.ndigits;
    if (ndigits == 0)
    {
        *result = 0;
        free_var(&rounded);
        return true;
    }

    /*
     * For input like 10000000000, we must treat stripped digits as real. So
     * the loop assumes there are weight+1 digits before the decimal point.
     */
    weight = rounded.weight;
    Assert(weight >= 0 && ndigits <= weight + 1);

    /* Construct the result */
    digits = rounded.digits;
    neg = (rounded.sign == NUMERIC_NEG);
    val = digits[0];
    for (i = 1; i <= weight; i++)
    {
        oldval = val;
        val *= NBASE;
        if (i < ndigits)
            val += digits[i];

        /*
         * The overflow check is a bit tricky because we want to accept
         * INT64_MIN, which will overflow the positive accumulator.  We can
         * detect this case easily though because INT64_MIN is the only
         * nonzero value for which -val == val (on a two's complement machine,
         * anyway).
         */
        if ((val / NBASE) != oldval)    /* possible overflow? */
        {
            if (!neg || (-val) != val || val == 0 || oldval < 0)
            {
                free_var(&rounded);
                return false;
            }
        }
    }

    free_var(&rounded);

    *result = neg ? -val : val;
    return true;
}

static void power_var ( NumericVar base,
NumericVar exp,
NumericVar result 
) [static]

Definition at line 5543 of file numeric.c.

References cmp_var(), DEC_DIGITS, NumericVar::dscale, exp_var(), free_var(), init_var, ln_var(), Max, Min, mul_var(), NumericVar::ndigits, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MAX_RESULT_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, numericvar_to_double_no_overflow(), numericvar_to_int8(), power_var_int(), set_var_from_var(), val, and NumericVar::weight.

Referenced by numeric_power().

{
    NumericVar  ln_base;
    NumericVar  ln_num;
    int         dec_digits;
    int         rscale;
    int         local_rscale;
    double      val;

    /* If exp can be represented as an integer, use power_var_int */
    if (exp->ndigits == 0 || exp->ndigits <= exp->weight + 1)
    {
        /* exact integer, but does it fit in int? */
        int64       expval64;

        if (numericvar_to_int8(exp, &expval64))
        {
            int         expval = (int) expval64;

            /* Test for overflow by reverse-conversion. */
            if ((int64) expval == expval64)
            {
                /* Okay, select rscale */
                rscale = NUMERIC_MIN_SIG_DIGITS;
                rscale = Max(rscale, base->dscale);
                rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
                rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

                power_var_int(base, expval, result, rscale);
                return;
            }
        }
    }

    /*
     * This avoids log(0) for cases of 0 raised to a non-integer. 0 ^ 0
     * handled by power_var_int().
     */
    if (cmp_var(base, &const_zero) == 0)
    {
        set_var_from_var(&const_zero, result);
        result->dscale = NUMERIC_MIN_SIG_DIGITS;        /* no need to round */
        return;
    }

    init_var(&ln_base);
    init_var(&ln_num);

    /* Set scale for ln() calculation --- need extra accuracy here */

    /* Approx decimal digits before decimal point */
    dec_digits = (base->weight + 1) * DEC_DIGITS;

    if (dec_digits > 1)
        rscale = NUMERIC_MIN_SIG_DIGITS * 2 - (int) log10(dec_digits - 1);
    else if (dec_digits < 1)
        rscale = NUMERIC_MIN_SIG_DIGITS * 2 - (int) log10(1 - dec_digits);
    else
        rscale = NUMERIC_MIN_SIG_DIGITS * 2;

    rscale = Max(rscale, base->dscale * 2);
    rscale = Max(rscale, exp->dscale * 2);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE * 2);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE * 2);

    local_rscale = rscale + 8;

    ln_var(base, &ln_base, local_rscale);

    mul_var(&ln_base, exp, &ln_num, local_rscale);

    /* Set scale for exp() -- compare numeric_exp() */

    /* convert input to float8, ignoring overflow */
    val = numericvar_to_double_no_overflow(&ln_num);

    /*
     * log10(result) = num * log10(e), so this is approximately the weight:
     */
    val *= 0.434294481903252;

    /* limit to something that won't cause integer overflow */
    val = Max(val, -NUMERIC_MAX_RESULT_SCALE);
    val = Min(val, NUMERIC_MAX_RESULT_SCALE);

    rscale = NUMERIC_MIN_SIG_DIGITS - (int) val;
    rscale = Max(rscale, base->dscale);
    rscale = Max(rscale, exp->dscale);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

    exp_var(&ln_num, result, rscale);

    free_var(&ln_num);
    free_var(&ln_base);
}

static void power_var_int ( NumericVar base,
int  exp,
NumericVar result,
int  rscale 
) [static]

Definition at line 5646 of file numeric.c.

References Abs, div_var(), div_var_fast(), NumericVar::dscale, free_var(), init_var, MUL_GUARD_DIGITS, mul_var(), round_var(), and set_var_from_var().

Referenced by exp_var(), get_str_from_var_sci(), and power_var().

{
    bool        neg;
    NumericVar  base_prod;
    int         local_rscale;

    switch (exp)
    {
        case 0:

            /*
             * While 0 ^ 0 can be either 1 or indeterminate (error), we treat
             * it as 1 because most programming languages do this. SQL:2003
             * also requires a return value of 1.
             * http://en.wikipedia.org/wiki/Exponentiation#Zero_to_the_zero_pow
             * er
             */
            set_var_from_var(&const_one, result);
            result->dscale = rscale;    /* no need to round */
            return;
        case 1:
            set_var_from_var(base, result);
            round_var(result, rscale);
            return;
        case -1:
            div_var(&const_one, base, result, rscale, true);
            return;
        case 2:
            mul_var(base, base, result, rscale);
            return;
        default:
            break;
    }

    /*
     * The general case repeatedly multiplies base according to the bit
     * pattern of exp.  We do the multiplications with some extra precision.
     */
    neg = (exp < 0);
    exp = Abs(exp);

    local_rscale = rscale + MUL_GUARD_DIGITS * 2;

    init_var(&base_prod);
    set_var_from_var(base, &base_prod);

    if (exp & 1)
        set_var_from_var(base, result);
    else
        set_var_from_var(&const_one, result);

    while ((exp >>= 1) > 0)
    {
        mul_var(&base_prod, &base_prod, &base_prod, local_rscale);
        if (exp & 1)
            mul_var(&base_prod, result, result, local_rscale);
    }

    free_var(&base_prod);

    /* Compensate for input sign, and round to requested rscale */
    if (neg)
        div_var_fast(&const_one, result, result, rscale, true);
    else
        round_var(result, rscale);
}

static void round_var ( NumericVar var,
int  rscale 
) [static]

Definition at line 5978 of file numeric.c.

References Assert, NumericVar::buf, DEC_DIGITS, NumericVar::digits, NumericVar::dscale, NBASE, NumericVar::ndigits, round_powers, NumericVar::sign, and NumericVar::weight.

Referenced by apply_typmod(), div_var(), div_var_fast(), exp_var(), mul_var(), numeric_round(), numericvar_to_int8(), power_var_int(), and sqrt_var().

{
    NumericDigit *digits = var->digits;
    int         di;
    int         ndigits;
    int         carry;

    var->dscale = rscale;

    /* decimal digits wanted */
    di = (var->weight + 1) * DEC_DIGITS + rscale;

    /*
     * If di = 0, the value loses all digits, but could round up to 1 if its
     * first extra digit is >= 5.  If di < 0 the result must be 0.
     */
    if (di < 0)
    {
        var->ndigits = 0;
        var->weight = 0;
        var->sign = NUMERIC_POS;
    }
    else
    {
        /* NBASE digits wanted */
        ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS;

        /* 0, or number of decimal digits to keep in last NBASE digit */
        di %= DEC_DIGITS;

        if (ndigits < var->ndigits ||
            (ndigits == var->ndigits && di > 0))
        {
            var->ndigits = ndigits;

#if DEC_DIGITS == 1
            /* di must be zero */
            carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0;
#else
            if (di == 0)
                carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0;
            else
            {
                /* Must round within last NBASE digit */
                int         extra,
                            pow10;

#if DEC_DIGITS == 4
                pow10 = round_powers[di];
#elif DEC_DIGITS == 2
                pow10 = 10;
#else
#error unsupported NBASE
#endif
                extra = digits[--ndigits] % pow10;
                digits[ndigits] -= extra;
                carry = 0;
                if (extra >= pow10 / 2)
                {
                    pow10 += digits[ndigits];
                    if (pow10 >= NBASE)
                    {
                        pow10 -= NBASE;
                        carry = 1;
                    }
                    digits[ndigits] = pow10;
                }
            }
#endif

            /* Propagate carry if needed */
            while (carry)
            {
                carry += digits[--ndigits];
                if (carry >= NBASE)
                {
                    digits[ndigits] = carry - NBASE;
                    carry = 1;
                }
                else
                {
                    digits[ndigits] = carry;
                    carry = 0;
                }
            }

            if (ndigits < 0)
            {
                Assert(ndigits == -1);  /* better not have added > 1 digit */
                Assert(var->digits > var->buf);
                var->digits--;
                var->ndigits++;
                var->weight++;
            }
        }
    }
}

static int select_div_scale ( NumericVar var1,
NumericVar var2 
) [static]

Definition at line 5016 of file numeric.c.

References NumericVar::digits, NumericVar::dscale, Max, Min, NumericVar::ndigits, NUMERIC_MAX_DISPLAY_SCALE, NUMERIC_MIN_DISPLAY_SCALE, NUMERIC_MIN_SIG_DIGITS, and NumericVar::weight.

Referenced by compute_bucket(), log_var(), numeric_div(), and numeric_stddev_internal().

{
    int         weight1,
                weight2,
                qweight,
                i;
    NumericDigit firstdigit1,
                firstdigit2;
    int         rscale;

    /*
     * The result scale of a division isn't specified in any SQL standard. For
     * PostgreSQL we select a result 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 result scale */
    rscale = NUMERIC_MIN_SIG_DIGITS - qweight * DEC_DIGITS;
    rscale = Max(rscale, var1->dscale);
    rscale = Max(rscale, var2->dscale);
    rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
    rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);

    return rscale;
}

static void set_var_from_num ( Numeric  value,
NumericVar dest 
) [static]

Definition at line 3367 of file numeric.c.

References alloc_var(), NumericVar::digits, NumericVar::dscale, NUMERIC_DIGITS, NUMERIC_DSCALE, NUMERIC_NDIGITS, NUMERIC_SIGN, NUMERIC_WEIGHT, NumericVar::sign, and NumericVar::weight.

Referenced by numeric(), numeric_round(), and numeric_trunc().

{
    int         ndigits;

    ndigits = NUMERIC_NDIGITS(num);

    alloc_var(dest, ndigits);

    dest->weight = NUMERIC_WEIGHT(num);
    dest->sign = NUMERIC_SIGN(num);
    dest->dscale = NUMERIC_DSCALE(num);

    memcpy(dest->digits, NUMERIC_DIGITS(num), ndigits * sizeof(NumericDigit));
}

static const char * set_var_from_str ( const char *  str,
const char *  cp,
NumericVar dest 
) [static]

Definition at line 3213 of file numeric.c.

References alloc_var(), DEC_DIGITS, NumericVar::digits, NumericVar::dscale, ereport, errcode(), errmsg(), ERROR, NUMERIC_MAX_PRECISION, palloc(), pfree(), NumericVar::sign, sign, strip_var(), and NumericVar::weight.

Referenced by float4_numeric(), float8_numeric(), and numeric_in().

{
    bool        have_dp = FALSE;
    int         i;
    unsigned char *decdigits;
    int         sign = NUMERIC_POS;
    int         dweight = -1;
    int         ddigits;
    int         dscale = 0;
    int         weight;
    int         ndigits;
    int         offset;
    NumericDigit *digits;

    /*
     * We first parse the string to extract decimal digits and determine the
     * correct decimal weight.  Then convert to NBASE representation.
     */
    switch (*cp)
    {
        case '+':
            sign = NUMERIC_POS;
            cp++;
            break;

        case '-':
            sign = NUMERIC_NEG;
            cp++;
            break;
    }

    if (*cp == '.')
    {
        have_dp = TRUE;
        cp++;
    }

    if (!isdigit((unsigned char) *cp))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
              errmsg("invalid input syntax for type numeric: \"%s\"", str)));

    decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);

    /* leading padding for digit alignment later */
    memset(decdigits, 0, DEC_DIGITS);
    i = DEC_DIGITS;

    while (*cp)
    {
        if (isdigit((unsigned char) *cp))
        {
            decdigits[i++] = *cp++ - '0';
            if (!have_dp)
                dweight++;
            else
                dscale++;
        }
        else if (*cp == '.')
        {
            if (have_dp)
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                      errmsg("invalid input syntax for type numeric: \"%s\"",
                             str)));
            have_dp = TRUE;
            cp++;
        }
        else
            break;
    }

    ddigits = i - DEC_DIGITS;
    /* trailing padding for digit alignment later */
    memset(decdigits + i, 0, DEC_DIGITS - 1);

    /* Handle exponent, if any */
    if (*cp == 'e' || *cp == 'E')
    {
        long        exponent;
        char       *endptr;

        cp++;
        exponent = strtol(cp, &endptr, 10);
        if (endptr == cp)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid input syntax for type numeric: \"%s\"",
                            str)));
        cp = endptr;
        if (exponent > NUMERIC_MAX_PRECISION ||
            exponent < -NUMERIC_MAX_PRECISION)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid input syntax for type numeric: \"%s\"",
                            str)));
        dweight += (int) exponent;
        dscale -= (int) exponent;
        if (dscale < 0)
            dscale = 0;
    }

    /*
     * Okay, convert pure-decimal representation to base NBASE.  First we need
     * to determine the converted weight and ndigits.  offset is the number of
     * decimal zeroes to insert before the first given digit to have a
     * correctly aligned first NBASE digit.
     */
    if (dweight >= 0)
        weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1;
    else
        weight = -((-dweight - 1) / DEC_DIGITS + 1);
    offset = (weight + 1) * DEC_DIGITS - (dweight + 1);
    ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS;

    alloc_var(dest, ndigits);
    dest->sign = sign;
    dest->weight = weight;
    dest->dscale = dscale;

    i = DEC_DIGITS - offset;
    digits = dest->digits;

    while (ndigits-- > 0)
    {
#if DEC_DIGITS == 4
        *digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 +
                     decdigits[i + 2]) * 10 + decdigits[i + 3];
#elif DEC_DIGITS == 2
        *digits++ = decdigits[i] * 10 + decdigits[i + 1];
#elif DEC_DIGITS == 1
        *digits++ = decdigits[i];
#else
#error unsupported NBASE
#endif
        i += DEC_DIGITS;
    }

    pfree(decdigits);

    /* Strip any leading/trailing zeroes, and normalize weight if zero */
    strip_var(dest);

    /* Return end+1 position for caller */
    return cp;
}

static void set_var_from_var ( NumericVar value,
NumericVar dest 
) [static]

Definition at line 3415 of file numeric.c.

References NumericVar::buf, digitbuf_alloc, digitbuf_free, NumericVar::digits, memmove, and NumericVar::ndigits.

Referenced by ceil_var(), exp_var(), exp_var_internal(), floor_var(), ln_var(), numeric_power(), numeric_sign(), numericvar_to_int8(), power_var(), power_var_int(), sqrt_var(), and width_bucket_numeric().

{
    NumericDigit *newbuf;

    newbuf = digitbuf_alloc(value->ndigits + 1);
    newbuf[0] = 0;              /* spare digit for rounding */
    memcpy(newbuf + 1, value->digits, value->ndigits * sizeof(NumericDigit));

    digitbuf_free(dest->buf);

    memmove(dest, value, sizeof(NumericVar));
    dest->buf = newbuf;
    dest->digits = newbuf + 1;
}

static void sqrt_var ( NumericVar arg,
NumericVar result,
int  rscale 
) [static]

Definition at line 5161 of file numeric.c.

References add_var(), alloc_var(), cmp_var(), NumericVar::digits, div_var_fast(), NumericVar::dscale, ereport, errcode(), errmsg(), ERROR, free_var(), init_var, mul_var(), round_var(), set_var_from_var(), NumericVar::sign, NumericVar::weight, and zero_var().

Referenced by ln_var(), numeric_sqrt(), and numeric_stddev_internal().

{
    NumericVar  tmp_arg;
    NumericVar  tmp_val;
    NumericVar  last_val;
    int         local_rscale;
    int         stat;

    local_rscale = rscale + 8;

    stat = cmp_var(arg, &const_zero);
    if (stat == 0)
    {
        zero_var(result);
        result->dscale = rscale;
        return;
    }

    /*
     * SQL2003 defines sqrt() in terms of power, so we need to emit the right
     * SQLSTATE error code if the operand is negative.
     */
    if (stat < 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
                 errmsg("cannot take square root of a negative number")));

    init_var(&tmp_arg);
    init_var(&tmp_val);
    init_var(&last_val);

    /* Copy arg in case it is the same var as result */
    set_var_from_var(arg, &tmp_arg);

    /*
     * Initialize the result to the first guess
     */
    alloc_var(result, 1);
    result->digits[0] = tmp_arg.digits[0] / 2;
    if (result->digits[0] == 0)
        result->digits[0] = 1;
    result->weight = tmp_arg.weight / 2;
    result->sign = NUMERIC_POS;

    set_var_from_var(result, &last_val);

    for (;;)
    {
        div_var_fast(&tmp_arg, result, &tmp_val, local_rscale, true);

        add_var(result, &tmp_val, result);
        mul_var(result, &const_zero_point_five, result, local_rscale);

        if (cmp_var(&last_val, result) == 0)
            break;
        set_var_from_var(result, &last_val);
    }

    free_var(&last_val);
    free_var(&tmp_val);
    free_var(&tmp_arg);

    /* Round to requested precision */
    round_var(result, rscale);
}

static void strip_var ( NumericVar var  )  [static]

Definition at line 6146 of file numeric.c.

References NumericVar::digits, NumericVar::ndigits, NumericVar::sign, and NumericVar::weight.

Referenced by add_abs(), div_var(), div_var_fast(), mul_var(), numericvar_to_int8(), set_var_from_str(), and sub_abs().

{
    NumericDigit *digits = var->digits;
    int         ndigits = var->ndigits;

    /* Strip leading zeroes */
    while (ndigits > 0 && *digits == 0)
    {
        digits++;
        var->weight--;
        ndigits--;
    }

    /* Strip trailing zeroes */
    while (ndigits > 0 && digits[ndigits - 1] == 0)
        ndigits--;

    /* If it's zero, normalize the sign and weight */
    if (ndigits == 0)
    {
        var->sign = NUMERIC_POS;
        var->weight = 0;
    }

    var->digits = digits;
    var->ndigits = ndigits;
}

static void sub_abs ( NumericVar var1,
NumericVar var2,
NumericVar result 
) [static]

Definition at line 5896 of file numeric.c.

References Assert, NumericVar::buf, digitbuf_alloc, digitbuf_free, NumericVar::digits, NumericVar::dscale, Max, NumericVar::ndigits, strip_var(), and NumericVar::weight.

Referenced by add_var(), and sub_var().

{
    NumericDigit *res_buf;
    NumericDigit *res_digits;
    int         res_ndigits;
    int         res_weight;
    int         res_rscale,
                rscale1,
                rscale2;
    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_dscale = Max(var1->dscale, var2->dscale);

    /* Note: here we are figuring rscale in base-NBASE digits */
    rscale1 = var1->ndigits - var1->weight - 1;
    rscale2 = var2->ndigits - var2->weight - 1;
    res_rscale = Max(rscale1, rscale2);

    res_ndigits = res_rscale + res_weight + 1;
    if (res_ndigits <= 0)
        res_ndigits = 1;

    res_buf = digitbuf_alloc(res_ndigits + 1);
    res_buf[0] = 0;             /* spare digit for later rounding */
    res_digits = res_buf + 1;

    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 + NBASE;
            borrow = -1;
        }
        else
        {
            res_digits[i] = borrow;
            borrow = 0;
        }
    }

    Assert(borrow == 0);        /* else caller gave us var1 < var2 */

    digitbuf_free(result->buf);
    result->ndigits = res_ndigits;
    result->buf = res_buf;
    result->digits = res_digits;
    result->weight = res_weight;
    result->dscale = res_dscale;

    /* Remove leading/trailing zeroes */
    strip_var(result);
}

static void sub_var ( NumericVar var1,
NumericVar var2,
NumericVar result 
) [static]

Definition at line 4190 of file numeric.c.

References add_abs(), cmp_abs(), NumericVar::dscale, Max, NUMERIC_NEG, NUMERIC_POS, NumericVar::sign, sub_abs(), and zero_var().

Referenced by compute_bucket(), floor_var(), ln_var(), mod_var(), numeric_stddev_internal(), and numeric_sub().

{
    /*
     * 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))
             * ----------
             */
            add_abs(var1, var2, result);
            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->dscale = Max(var1->dscale, var2->dscale);
                    break;

                case 1:
                    /* ----------
                     * ABS(var1) > ABS(var2)
                     * result = +(ABS(var1) - ABS(var2))
                     * ----------
                     */
                    sub_abs(var1, var2, result);
                    result->sign = NUMERIC_POS;
                    break;

                case -1:
                    /* ----------
                     * ABS(var1) < ABS(var2)
                     * result = -(ABS(var2) - ABS(var1))
                     * ----------
                     */
                    sub_abs(var2, var1, result);
                    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->dscale = Max(var1->dscale, var2->dscale);
                    break;

                case 1:
                    /* ----------
                     * ABS(var1) > ABS(var2)
                     * result = -(ABS(var1) - ABS(var2))
                     * ----------
                     */
                    sub_abs(var1, var2, result);
                    result->sign = NUMERIC_NEG;
                    break;

                case -1:
                    /* ----------
                     * ABS(var1) < ABS(var2)
                     * result = +(ABS(var2) - ABS(var1))
                     * ----------
                     */
                    sub_abs(var2, var1, result);
                    result->sign = NUMERIC_POS;
                    break;
            }
        }
        else
        {
            /* ----------
             * var1 is negative, var2 is positive
             * result = -(ABS(var1) + ABS(var2))
             * ----------
             */
            add_abs(var1, var2, result);
            result->sign = NUMERIC_NEG;
        }
    }
}

static void trunc_var ( NumericVar var,
int  rscale 
) [static]

Definition at line 6084 of file numeric.c.

References DEC_DIGITS, NumericVar::digits, NumericVar::dscale, NumericVar::ndigits, round_powers, NumericVar::sign, and NumericVar::weight.

Referenced by ceil_var(), div_var(), div_var_fast(), floor_var(), numeric_power(), and numeric_trunc().

{
    int         di;
    int         ndigits;

    var->dscale = rscale;

    /* decimal digits wanted */
    di = (var->weight + 1) * DEC_DIGITS + rscale;

    /*
     * If di <= 0, the value loses all digits.
     */
    if (di <= 0)
    {
        var->ndigits = 0;
        var->weight = 0;
        var->sign = NUMERIC_POS;
    }
    else
    {
        /* NBASE digits wanted */
        ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS;

        if (ndigits <= var->ndigits)
        {
            var->ndigits = ndigits;

#if DEC_DIGITS == 1
            /* no within-digit stuff to worry about */
#else
            /* 0, or number of decimal digits to keep in last NBASE digit */
            di %= DEC_DIGITS;

            if (di > 0)
            {
                /* Must truncate within last NBASE digit */
                NumericDigit *digits = var->digits;
                int         extra,
                            pow10;

#if DEC_DIGITS == 4
                pow10 = round_powers[di];
#elif DEC_DIGITS == 2
                pow10 = 10;
#else
#error unsupported NBASE
#endif
                extra = digits[--ndigits] % pow10;
                digits[ndigits] -= extra;
            }
#endif
        }
    }
}

Datum width_bucket_numeric ( PG_FUNCTION_ARGS   ) 

Definition at line 1199 of file numeric.c.

References add_var(), cmp_numerics(), compute_bucket(), ereport, errcode(), errmsg(), ERROR, free_var(), init_var, int8_to_numericvar(), NUMERIC_IS_NAN, numericvar_to_int4(), PG_GETARG_INT32, PG_GETARG_NUMERIC, PG_RETURN_INT32, and set_var_from_var().

{
    Numeric     operand = PG_GETARG_NUMERIC(0);
    Numeric     bound1 = PG_GETARG_NUMERIC(1);
    Numeric     bound2 = PG_GETARG_NUMERIC(2);
    int32       count = PG_GETARG_INT32(3);
    NumericVar  count_var;
    NumericVar  result_var;
    int32       result;

    if (count <= 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
                 errmsg("count must be greater than zero")));

    if (NUMERIC_IS_NAN(operand) ||
        NUMERIC_IS_NAN(bound1) ||
        NUMERIC_IS_NAN(bound2))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
             errmsg("operand, lower bound, and upper bound cannot be NaN")));

    init_var(&result_var);
    init_var(&count_var);

    /* Convert 'count' to a numeric, for ease of use later */
    int8_to_numericvar((int64) count, &count_var);

    switch (cmp_numerics(bound1, bound2))
    {
        case 0:
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
                 errmsg("lower bound cannot equal upper bound")));

            /* bound1 < bound2 */
        case -1:
            if (cmp_numerics(operand, bound1) < 0)
                set_var_from_var(&const_zero, &result_var);
            else if (cmp_numerics(operand, bound2) >= 0)
                add_var(&count_var, &const_one, &result_var);
            else
                compute_bucket(operand, bound1, bound2,
                               &count_var, &result_var);
            break;

            /* bound1 > bound2 */
        case 1:
            if (cmp_numerics(operand, bound1) > 0)
                set_var_from_var(&const_zero, &result_var);
            else if (cmp_numerics(operand, bound2) <= 0)
                add_var(&count_var, &const_one, &result_var);
            else
                compute_bucket(operand, bound1, bound2,
                               &count_var, &result_var);
            break;
    }

    /* if result exceeds the range of a legal int4, we ereport here */
    result = numericvar_to_int4(&result_var);

    free_var(&count_var);
    free_var(&result_var);

    PG_RETURN_INT32(result);
}

static void zero_var ( NumericVar var  )  [static]

Definition at line 3189 of file numeric.c.

References NumericVar::buf, digitbuf_free, NumericVar::digits, NumericVar::ndigits, NumericVar::sign, and NumericVar::weight.

Referenced by add_var(), div_var(), div_var_fast(), mul_var(), sqrt_var(), and sub_var().

{
    digitbuf_free(var->buf);
    var->buf = NULL;
    var->digits = NULL;
    var->ndigits = 0;
    var->weight = 0;            /* by convention; doesn't really matter */
    var->sign = NUMERIC_POS;    /* anything but NAN... */
}


Variable Documentation

Initial value:
{0, 0, NUMERIC_NAN, 0, NULL, NULL}

Definition at line 333 of file numeric.c.

Initial value:
{1, 0, NUMERIC_POS, 0, NULL, const_one_data}

Definition at line 272 of file numeric.c.

NumericDigit const_one_data[1] = {1} [static]

Definition at line 271 of file numeric.c.

Initial value:
{2, 0, NUMERIC_POS, 1, NULL, const_one_point_one_data}

Definition at line 330 of file numeric.c.

NumericDigit const_one_point_one_data[2] = {1, 1000} [static]

Definition at line 324 of file numeric.c.

Initial value:
{1, 0, NUMERIC_POS, 0, NULL, const_ten_data}

Definition at line 281 of file numeric.c.

NumericDigit const_ten_data[1] = {10} [static]

Definition at line 280 of file numeric.c.

Initial value:
{1, 0, NUMERIC_POS, 0, NULL, const_two_data}

Definition at line 276 of file numeric.c.

NumericDigit const_two_data[1] = {2} [static]

Definition at line 275 of file numeric.c.

Initial value:
{0, 0, NUMERIC_POS, 0, NULL, const_zero_data}

Definition at line 268 of file numeric.c.

NumericDigit const_zero_data[1] = {0} [static]

Definition at line 267 of file numeric.c.

Initial value:
{1, -1, NUMERIC_POS, 2, NULL, const_zero_point_01_data}

Definition at line 311 of file numeric.c.

Definition at line 310 of file numeric.c.

Initial value:
{1, -1, NUMERIC_POS, 1, NULL, const_zero_point_five_data}

Definition at line 296 of file numeric.c.

Definition at line 290 of file numeric.c.

Initial value:
{1, -1, NUMERIC_POS, 1, NULL, const_zero_point_nine_data}

Definition at line 306 of file numeric.c.

Definition at line 300 of file numeric.c.

const int round_powers[4] = {0, 1000, 100, 10} [static]

Definition at line 337 of file numeric.c.

Referenced by round_var(), and trunc_var().