1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

Support EEEE (scientific notation) in to_char().

Pavel Stehule, Brendan Jurd
This commit is contained in:
Tom Lane
2009-08-10 18:29:27 +00:00
parent 933b17b663
commit e61fd4ac74
6 changed files with 347 additions and 14 deletions

View File

@ -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);
@ -426,6 +427,32 @@ numeric_out(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(str);
}
/*
* 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
*
@ -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() -