diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2009-08-10 18:29:27 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2009-08-10 18:29:27 +0000 |
commit | e61fd4ac7414a52714c3288889cc754d2e11c4a8 (patch) | |
tree | a3bf339b96dc1d3f5fde9c2fdb7b8d03efdd6b95 /src/backend/utils/adt/numeric.c | |
parent | 933b17b663f06bd5bbe24e8e12044a844fd79bbf (diff) | |
download | postgresql-e61fd4ac7414a52714c3288889cc754d2e11c4a8.tar.gz postgresql-e61fd4ac7414a52714c3288889cc754d2e11c4a8.zip |
Support EEEE (scientific notation) in to_char().
Pavel Stehule, Brendan Jurd
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r-- | src/backend/utils/adt/numeric.c | 133 |
1 files changed, 132 insertions, 1 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index ba3721b12b3..29b00e08e00 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -14,7 +14,7 @@ * Copyright (c) 1998-2009, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.118 2009/06/11 14:49:03 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.119 2009/08/10 18:29:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -247,6 +247,7 @@ static const char *set_var_from_str(const char *str, const char *cp, static void set_var_from_num(Numeric value, NumericVar *dest); static void set_var_from_var(NumericVar *value, NumericVar *dest); static char *get_str_from_var(NumericVar *var, int dscale); +static char *get_str_from_var_sci(NumericVar *var, int rscale); static Numeric make_result(NumericVar *var); @@ -427,6 +428,32 @@ numeric_out(PG_FUNCTION_ARGS) } /* + * numeric_out_sci() - + * + * Output function for numeric data type in scientific notation. + */ +char * +numeric_out_sci(Numeric num, int scale) +{ + NumericVar x; + char *str; + + /* + * Handle NaN + */ + if (NUMERIC_IS_NAN(num)) + return pstrdup("NaN"); + + init_var(&x); + set_var_from_num(num, &x); + + str = get_str_from_var_sci(&x, scale); + + free_var(&x); + return str; +} + +/* * numeric_recv - converts external binary format to numeric * * External format is a sequence of int16's: @@ -3364,6 +3391,110 @@ get_str_from_var(NumericVar *var, int dscale) return str; } +/* + * get_str_from_var_sci() - + * + * Convert a var to a normalised scientific notation text representation. + * This function does the heavy lifting for numeric_out_sci(). + * + * This notation has the general form a * 10^b, where a is known as the + * "significand" and b is known as the "exponent". + * + * Because we can't do superscript in ASCII (and because we want to copy + * printf's behaviour) we display the exponent using E notation, with a + * minimum of two exponent digits. + * + * For example, the value 1234 could be output as 1.2e+03. + * + * We assume that the exponent can fit into an int32. + * + * rscale is the number of decimal digits desired after the decimal point in + * the output, negative values will be treated as meaning zero. + * + * CAUTION: var's contents may be modified by rounding! + * + * Returns a palloc'd string. + */ +static char * +get_str_from_var_sci(NumericVar *var, int rscale) +{ + 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); + + int8_to_numericvar((int64) 10, &denominator); + power_var_int(&denominator, exponent, &denominator, denom_scale); + div_var(var, &denominator, &significand, rscale, true); + sig_out = get_str_from_var(&significand, rscale); + + 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; +} + /* * make_result() - |