mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
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>
|
<entry><literal>6.0000000000</literal></entry>
|
||||||
</row>
|
</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>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<indexterm>
|
<indexterm>
|
||||||
@ -1011,8 +1025,8 @@
|
|||||||
</entry>
|
</entry>
|
||||||
<entry><type>integer</type></entry>
|
<entry><type>integer</type></entry>
|
||||||
<entry>scale of the argument (the number of decimal digits in the fractional part)</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>scale(8.4100)</literal></entry>
|
||||||
<entry><literal>2</literal></entry>
|
<entry><literal>4</literal></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
@ -1041,6 +1055,20 @@
|
|||||||
<entry><literal>1.4142135623731</literal></entry>
|
<entry><literal>1.4142135623731</literal></entry>
|
||||||
</row>
|
</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>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<indexterm>
|
<indexterm>
|
||||||
|
@ -3179,6 +3179,97 @@ numeric_scale(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(NUMERIC_DSCALE(num));
|
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 */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201911241
|
#define CATALOG_VERSION_NO 202001061
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4254,6 +4254,13 @@
|
|||||||
{ oid => '3281', descr => 'number of decimal digits in the fractional part',
|
{ oid => '3281', descr => 'number of decimal digits in the fractional part',
|
||||||
proname => 'scale', prorettype => 'int4', proargtypes => 'numeric',
|
proname => 'scale', prorettype => 'int4', proargtypes => 'numeric',
|
||||||
prosrc => 'numeric_scale' },
|
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',
|
{ oid => '1740', descr => 'convert int4 to numeric',
|
||||||
proname => 'numeric', prorettype => 'numeric', proargtypes => 'int4',
|
proname => 'numeric', prorettype => 'numeric', proargtypes => 'int4',
|
||||||
prosrc => 'int4_numeric' },
|
prosrc => 'int4_numeric' },
|
||||||
|
@ -2078,6 +2078,132 @@ select scale(-13.000000000000000);
|
|||||||
15
|
15
|
||||||
(1 row)
|
(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()
|
-- Tests for SUM()
|
||||||
--
|
--
|
||||||
|
@ -1036,6 +1036,36 @@ select scale(110123.12475871856128);
|
|||||||
select scale(-1123.12471856128);
|
select scale(-1123.12471856128);
|
||||||
select scale(-13.000000000000000);
|
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()
|
-- Tests for SUM()
|
||||||
--
|
--
|
||||||
|
Loading…
x
Reference in New Issue
Block a user