mirror of https://github.com/postgres/postgres
Add functions min_scale(numeric) and trim_scale(numeric).
These allow better control of trailing zeroes in numeric values. Pavel Stehule, based on an old proposal of Marko Tiikkaja's; review by Karl Pinc Discussion: https://postgr.es/m/CAFj8pRDjs-navGASeF0Wk74N36YGFJ+v=Ok9_knRa7vDc-qugg@mail.gmail.com
This commit is contained in:
parent
b9c130a1fd
commit
20d6225d16
|
@ -918,6 +918,20 @@
|
|||
<entry><literal>6.0000000000</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>min_scale</primary>
|
||||
</indexterm>
|
||||
<literal><function>min_scale(<type>numeric</type>)</function></literal>
|
||||
</entry>
|
||||
<entry><type>integer</type></entry>
|
||||
<entry>minimum scale (number of fractional decimal digits) needed
|
||||
to represent the supplied value</entry>
|
||||
<entry><literal>min_scale(8.4100)</literal></entry>
|
||||
<entry><literal>2</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
|
@ -1011,8 +1025,8 @@
|
|||
</entry>
|
||||
<entry><type>integer</type></entry>
|
||||
<entry>scale of the argument (the number of decimal digits in the fractional part)</entry>
|
||||
<entry><literal>scale(8.41)</literal></entry>
|
||||
<entry><literal>2</literal></entry>
|
||||
<entry><literal>scale(8.4100)</literal></entry>
|
||||
<entry><literal>4</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
@ -1041,6 +1055,20 @@
|
|||
<entry><literal>1.4142135623731</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>trim_scale</primary>
|
||||
</indexterm>
|
||||
<literal><function>trim_scale(<type>numeric</type>)</function></literal>
|
||||
</entry>
|
||||
<entry><type>numeric</type></entry>
|
||||
<entry>reduce the scale (number of fractional decimal digits) by
|
||||
removing trailing zeroes</entry>
|
||||
<entry><literal>trim_scale(8.4100)</literal></entry>
|
||||
<entry><literal>8.41</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
|
|
|
@ -3179,6 +3179,97 @@ numeric_scale(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_INT32(NUMERIC_DSCALE(num));
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate minimum scale for value.
|
||||
*/
|
||||
static int
|
||||
get_min_scale(NumericVar *var)
|
||||
{
|
||||
int min_scale;
|
||||
int last_digit_pos;
|
||||
|
||||
/*
|
||||
* Ordinarily, the input value will be "stripped" so that the last
|
||||
* NumericDigit is nonzero. But we don't want to get into an infinite
|
||||
* loop if it isn't, so explicitly find the last nonzero digit.
|
||||
*/
|
||||
last_digit_pos = var->ndigits - 1;
|
||||
while (last_digit_pos >= 0 &&
|
||||
var->digits[last_digit_pos] == 0)
|
||||
last_digit_pos--;
|
||||
|
||||
if (last_digit_pos >= 0)
|
||||
{
|
||||
/* compute min_scale assuming that last ndigit has no zeroes */
|
||||
min_scale = (last_digit_pos - var->weight) * DEC_DIGITS;
|
||||
|
||||
/*
|
||||
* We could get a negative result if there are no digits after the
|
||||
* decimal point. In this case the min_scale must be zero.
|
||||
*/
|
||||
if (min_scale > 0)
|
||||
{
|
||||
/*
|
||||
* Reduce min_scale if trailing digit(s) in last NumericDigit are
|
||||
* zero.
|
||||
*/
|
||||
NumericDigit last_digit = var->digits[last_digit_pos];
|
||||
|
||||
while (last_digit % 10 == 0)
|
||||
{
|
||||
min_scale--;
|
||||
last_digit /= 10;
|
||||
}
|
||||
}
|
||||
else
|
||||
min_scale = 0;
|
||||
}
|
||||
else
|
||||
min_scale = 0; /* result if input is zero */
|
||||
|
||||
return min_scale;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns minimum scale required to represent supplied value without loss.
|
||||
*/
|
||||
Datum
|
||||
numeric_min_scale(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num = PG_GETARG_NUMERIC(0);
|
||||
NumericVar arg;
|
||||
int min_scale;
|
||||
|
||||
if (NUMERIC_IS_NAN(num))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
init_var_from_num(num, &arg);
|
||||
min_scale = get_min_scale(&arg);
|
||||
free_var(&arg);
|
||||
|
||||
PG_RETURN_INT32(min_scale);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reduce scale of numeric value to represent supplied value without loss.
|
||||
*/
|
||||
Datum
|
||||
numeric_trim_scale(PG_FUNCTION_ARGS)
|
||||
{
|
||||
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);
|
||||
result.dscale = get_min_scale(&result);
|
||||
res = make_result(&result);
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
|
|
|
@ -53,6 +53,6 @@
|
|||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201911241
|
||||
#define CATALOG_VERSION_NO 202001061
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4254,6 +4254,13 @@
|
|||
{ oid => '3281', descr => 'number of decimal digits in the fractional part',
|
||||
proname => 'scale', prorettype => 'int4', proargtypes => 'numeric',
|
||||
prosrc => 'numeric_scale' },
|
||||
{ oid => '8389', descr => 'minimum scale needed to represent the value',
|
||||
proname => 'min_scale', prorettype => 'int4', proargtypes => 'numeric',
|
||||
prosrc => 'numeric_min_scale' },
|
||||
{ oid => '8390',
|
||||
descr => 'numeric with minimum scale needed to represent the value',
|
||||
proname => 'trim_scale', prorettype => 'numeric', proargtypes => 'numeric',
|
||||
prosrc => 'numeric_trim_scale' },
|
||||
{ oid => '1740', descr => 'convert int4 to numeric',
|
||||
proname => 'numeric', prorettype => 'numeric', proargtypes => 'int4',
|
||||
prosrc => 'int4_numeric' },
|
||||
|
|
|
@ -2078,6 +2078,132 @@ select scale(-13.000000000000000);
|
|||
15
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Tests for min_scale()
|
||||
--
|
||||
select min_scale(numeric 'NaN') is NULL; -- should be true
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select min_scale(0); -- no digits
|
||||
min_scale
|
||||
-----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
select min_scale(0.00); -- no digits again
|
||||
min_scale
|
||||
-----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.0); -- no scale
|
||||
min_scale
|
||||
-----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.1); -- scale 1
|
||||
min_scale
|
||||
-----------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.12); -- scale 2
|
||||
min_scale
|
||||
-----------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.123); -- scale 3
|
||||
min_scale
|
||||
-----------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.1234); -- scale 4, filled digit
|
||||
min_scale
|
||||
-----------
|
||||
4
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.12345); -- scale 5, 2 NDIGITS
|
||||
min_scale
|
||||
-----------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
select min_scale(1.1000); -- 1 pos in NDIGITS
|
||||
min_scale
|
||||
-----------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
select min_scale(1e100); -- very big number
|
||||
min_scale
|
||||
-----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Tests for trim_scale()
|
||||
--
|
||||
select trim_scale(numeric 'NaN');
|
||||
trim_scale
|
||||
------------
|
||||
NaN
|
||||
(1 row)
|
||||
|
||||
select trim_scale(1.120);
|
||||
trim_scale
|
||||
------------
|
||||
1.12
|
||||
(1 row)
|
||||
|
||||
select trim_scale(0);
|
||||
trim_scale
|
||||
------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
select trim_scale(0.00);
|
||||
trim_scale
|
||||
------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
select trim_scale(1.1234500);
|
||||
trim_scale
|
||||
------------
|
||||
1.12345
|
||||
(1 row)
|
||||
|
||||
select trim_scale(110123.12475871856128000);
|
||||
trim_scale
|
||||
-----------------------
|
||||
110123.12475871856128
|
||||
(1 row)
|
||||
|
||||
select trim_scale(-1123.124718561280000000);
|
||||
trim_scale
|
||||
-------------------
|
||||
-1123.12471856128
|
||||
(1 row)
|
||||
|
||||
select trim_scale(-13.00000000000000000000);
|
||||
trim_scale
|
||||
------------
|
||||
-13
|
||||
(1 row)
|
||||
|
||||
select trim_scale(1e100);
|
||||
trim_scale
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Tests for SUM()
|
||||
--
|
||||
|
|
|
@ -1036,6 +1036,36 @@ select scale(110123.12475871856128);
|
|||
select scale(-1123.12471856128);
|
||||
select scale(-13.000000000000000);
|
||||
|
||||
--
|
||||
-- Tests for min_scale()
|
||||
--
|
||||
|
||||
select min_scale(numeric 'NaN') is NULL; -- should be true
|
||||
select min_scale(0); -- no digits
|
||||
select min_scale(0.00); -- no digits again
|
||||
select min_scale(1.0); -- no scale
|
||||
select min_scale(1.1); -- scale 1
|
||||
select min_scale(1.12); -- scale 2
|
||||
select min_scale(1.123); -- scale 3
|
||||
select min_scale(1.1234); -- scale 4, filled digit
|
||||
select min_scale(1.12345); -- scale 5, 2 NDIGITS
|
||||
select min_scale(1.1000); -- 1 pos in NDIGITS
|
||||
select min_scale(1e100); -- very big number
|
||||
|
||||
--
|
||||
-- Tests for trim_scale()
|
||||
--
|
||||
|
||||
select trim_scale(numeric 'NaN');
|
||||
select trim_scale(1.120);
|
||||
select trim_scale(0);
|
||||
select trim_scale(0.00);
|
||||
select trim_scale(1.1234500);
|
||||
select trim_scale(110123.12475871856128000);
|
||||
select trim_scale(-1123.124718561280000000);
|
||||
select trim_scale(-13.00000000000000000000);
|
||||
select trim_scale(1e100);
|
||||
|
||||
--
|
||||
-- Tests for SUM()
|
||||
--
|
||||
|
|
Loading…
Reference in New Issue