mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Add support for hyperbolic functions, as well as log10().
The SQL:2016 standard adds support for the hyperbolic functions sinh(), cosh(), and tanh(). POSIX has long required libm to provide those functions as well as their inverses asinh(), acosh(), atanh(). Hence, let's just expose the libm functions to the SQL level. As with the trig functions, we only implement versions for float8, not numeric. For the moment, we'll assume that all platforms actually do have these functions; if experience teaches otherwise, some autoconf effort may be needed. SQL:2016 also adds support for base-10 logarithm, but with the function name log10(), whereas the name we've long used is log(). Add aliases named log10() for the float8 and numeric versions. Lætitia Avrot Discussion: https://postgr.es/m/CAB_COdguG22LO=rnxDQ2DW1uzv8aQoUzyDQNJjrR4k00XSgm5w@mail.gmail.com
This commit is contained in:
parent
3aa0395d4e
commit
f1d85aa98e
@ -896,6 +896,19 @@
|
||||
<entry><literal>2</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>log10</primary>
|
||||
</indexterm>
|
||||
<literal><function>log10(<type>dp</type> or <type>numeric</type>)</function></literal>
|
||||
</entry>
|
||||
<entry>(same as input)</entry>
|
||||
<entry>base 10 logarithm</entry>
|
||||
<entry><literal>log10(100.0)</literal></entry>
|
||||
<entry><literal>2</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><literal><function>log(<parameter>b</parameter> <type>numeric</type>,
|
||||
<parameter>x</parameter> <type>numeric</type>)</function></literal></entry>
|
||||
@ -1147,8 +1160,8 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, <xref linkend="functions-math-trig-table"/> shows the
|
||||
available trigonometric functions. All trigonometric functions
|
||||
<xref linkend="functions-math-trig-table"/> shows the
|
||||
available trigonometric functions. All these functions
|
||||
take arguments and return values of type <type>double
|
||||
precision</type>. Each of the trigonometric functions comes in
|
||||
two variants, one that measures angles in radians and one that
|
||||
@ -1311,6 +1324,96 @@
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
<xref linkend="functions-math-hyp-table"/> shows the
|
||||
available hyperbolic functions. All these functions
|
||||
take arguments and return values of type <type>double
|
||||
precision</type>.
|
||||
</para>
|
||||
|
||||
<table id="functions-math-hyp-table">
|
||||
<title>Hyperbolic Functions</title>
|
||||
|
||||
<tgroup cols="4">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Function</entry>
|
||||
<entry>Description</entry>
|
||||
<entry>Example</entry>
|
||||
<entry>Result</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>sinh</primary>
|
||||
</indexterm>
|
||||
<literal><function>sinh(<replaceable>x</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>hyperbolic sine</entry>
|
||||
<entry><literal>sinh(0)</literal></entry>
|
||||
<entry><literal>0</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>cosh</primary>
|
||||
</indexterm>
|
||||
<literal><function>cosh(<replaceable>x</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>hyperbolic cosine</entry>
|
||||
<entry><literal>cosh(0)</literal></entry>
|
||||
<entry><literal>1</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>tanh</primary>
|
||||
</indexterm>
|
||||
<literal><function>tanh(<replaceable>x</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>hyperbolic tangent</entry>
|
||||
<entry><literal>tanh(0)</literal></entry>
|
||||
<entry><literal>0</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>asinh</primary>
|
||||
</indexterm>
|
||||
<literal><function>asinh(<replaceable>x</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>inverse hyperbolic sine</entry>
|
||||
<entry><literal>asinh(0)</literal></entry>
|
||||
<entry><literal>0</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>acosh</primary>
|
||||
</indexterm>
|
||||
<literal><function>acosh(<replaceable>x</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>inverse hyperbolic cosine</entry>
|
||||
<entry><literal>acosh(1)</literal></entry>
|
||||
<entry><literal>0</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<indexterm>
|
||||
<primary>atanh</primary>
|
||||
</indexterm>
|
||||
<literal><function>atanh(<replaceable>x</replaceable>)</function></literal>
|
||||
</entry>
|
||||
<entry>inverse hyperbolic tangent</entry>
|
||||
<entry><literal>atanh(0)</literal></entry>
|
||||
<entry><literal>0</literal></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
|
@ -230,9 +230,9 @@ float4in(PG_FUNCTION_ARGS)
|
||||
* detect whether it's a "real" out-of-range condition by checking
|
||||
* to see if the result is zero or huge.
|
||||
*
|
||||
* Use isinf() rather than HUGE_VALF on VS2013 because it generates
|
||||
* a spurious overflow warning for -HUGE_VALF. Also use isinf() if
|
||||
* HUGE_VALF is missing.
|
||||
* Use isinf() rather than HUGE_VALF on VS2013 because it
|
||||
* generates a spurious overflow warning for -HUGE_VALF. Also use
|
||||
* isinf() if HUGE_VALF is missing.
|
||||
*/
|
||||
if (val == 0.0 ||
|
||||
#if !defined(HUGE_VALF) || (defined(_MSC_VER) && (_MSC_VER < 1900))
|
||||
@ -2426,6 +2426,160 @@ radians(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
/* ========== HYPERBOLIC FUNCTIONS ========== */
|
||||
|
||||
|
||||
/*
|
||||
* dsinh - returns the hyperbolic sine of arg1
|
||||
*/
|
||||
Datum
|
||||
dsinh(PG_FUNCTION_ARGS)
|
||||
{
|
||||
float8 arg1 = PG_GETARG_FLOAT8(0);
|
||||
float8 result;
|
||||
|
||||
errno = 0;
|
||||
result = sinh(arg1);
|
||||
|
||||
/*
|
||||
* if an ERANGE error occurs, it means there is an overflow. For sinh,
|
||||
* the result should be either -infinity or infinity, depending on the
|
||||
* sign of arg1.
|
||||
*/
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
if (arg1 < 0)
|
||||
result = -get_float8_infinity();
|
||||
else
|
||||
result = get_float8_infinity();
|
||||
}
|
||||
|
||||
check_float8_val(result, true, true);
|
||||
PG_RETURN_FLOAT8(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* dcosh - returns the hyperbolic cosine of arg1
|
||||
*/
|
||||
Datum
|
||||
dcosh(PG_FUNCTION_ARGS)
|
||||
{
|
||||
float8 arg1 = PG_GETARG_FLOAT8(0);
|
||||
float8 result;
|
||||
|
||||
errno = 0;
|
||||
result = cosh(arg1);
|
||||
|
||||
/*
|
||||
* if an ERANGE error occurs, it means there is an overflow. As cosh is
|
||||
* always positive, it always means the result is positive infinity.
|
||||
*/
|
||||
if (errno == ERANGE)
|
||||
result = get_float8_infinity();
|
||||
|
||||
check_float8_val(result, true, false);
|
||||
PG_RETURN_FLOAT8(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* dtanh - returns the hyperbolic tangent of arg1
|
||||
*/
|
||||
Datum
|
||||
dtanh(PG_FUNCTION_ARGS)
|
||||
{
|
||||
float8 arg1 = PG_GETARG_FLOAT8(0);
|
||||
float8 result;
|
||||
|
||||
/*
|
||||
* For tanh, we don't need an errno check because it never overflows.
|
||||
*/
|
||||
result = tanh(arg1);
|
||||
|
||||
check_float8_val(result, false, true);
|
||||
PG_RETURN_FLOAT8(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* dasinh - returns the inverse hyperbolic sine of arg1
|
||||
*/
|
||||
Datum
|
||||
dasinh(PG_FUNCTION_ARGS)
|
||||
{
|
||||
float8 arg1 = PG_GETARG_FLOAT8(0);
|
||||
float8 result;
|
||||
|
||||
/*
|
||||
* For asinh, we don't need an errno check because it never overflows.
|
||||
*/
|
||||
result = asinh(arg1);
|
||||
|
||||
check_float8_val(result, true, true);
|
||||
PG_RETURN_FLOAT8(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* dacosh - returns the inverse hyperbolic cosine of arg1
|
||||
*/
|
||||
Datum
|
||||
dacosh(PG_FUNCTION_ARGS)
|
||||
{
|
||||
float8 arg1 = PG_GETARG_FLOAT8(0);
|
||||
float8 result;
|
||||
|
||||
/*
|
||||
* acosh is only defined for inputs >= 1.0. By checking this ourselves,
|
||||
* we need not worry about checking for an EDOM error, which is a good
|
||||
* thing because some implementations will report that for NaN. Otherwise,
|
||||
* no error is possible.
|
||||
*/
|
||||
if (arg1 < 1.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("input is out of range")));
|
||||
|
||||
result = acosh(arg1);
|
||||
|
||||
check_float8_val(result, true, true);
|
||||
PG_RETURN_FLOAT8(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* datanh - returns the inverse hyperbolic tangent of arg1
|
||||
*/
|
||||
Datum
|
||||
datanh(PG_FUNCTION_ARGS)
|
||||
{
|
||||
float8 arg1 = PG_GETARG_FLOAT8(0);
|
||||
float8 result;
|
||||
|
||||
/*
|
||||
* atanh is only defined for inputs between -1 and 1. By checking this
|
||||
* ourselves, we need not worry about checking for an EDOM error, which is
|
||||
* a good thing because some implementations will report that for NaN.
|
||||
*/
|
||||
if (arg1 < -1.0 || arg1 > 1.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("input is out of range")));
|
||||
|
||||
/*
|
||||
* Also handle the infinity cases ourselves; this is helpful because old
|
||||
* glibc versions may produce the wrong errno for this. All other inputs
|
||||
* cannot produce an error.
|
||||
*/
|
||||
if (arg1 == -1.0)
|
||||
result = -get_float8_infinity();
|
||||
else if (arg1 == 1.0)
|
||||
result = get_float8_infinity();
|
||||
else
|
||||
result = atanh(arg1);
|
||||
|
||||
check_float8_val(result, true, true);
|
||||
PG_RETURN_FLOAT8(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* drandom - returns a random number
|
||||
*/
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201903101
|
||||
#define CATALOG_VERSION_NO 201903121
|
||||
|
||||
#endif
|
||||
|
@ -2616,6 +2616,9 @@
|
||||
{ oid => '1340', descr => 'base 10 logarithm',
|
||||
proname => 'log', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dlog10' },
|
||||
{ oid => '1194', descr => 'base 10 logarithm',
|
||||
proname => 'log10', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dlog10' },
|
||||
{ oid => '1341', descr => 'natural logarithm',
|
||||
proname => 'ln', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dlog1' },
|
||||
@ -3282,6 +3285,25 @@
|
||||
{ oid => '1610', descr => 'PI',
|
||||
proname => 'pi', prorettype => 'float8', proargtypes => '', prosrc => 'dpi' },
|
||||
|
||||
{ oid => '2462', descr => 'hyperbolic sine',
|
||||
proname => 'sinh', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dsinh' },
|
||||
{ oid => '2463', descr => 'hyperbolic cosine',
|
||||
proname => 'cosh', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dcosh' },
|
||||
{ oid => '2464', descr => 'hyperbolic tangent',
|
||||
proname => 'tanh', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dtanh' },
|
||||
{ oid => '2465', descr => 'inverse hyperbolic sine',
|
||||
proname => 'asinh', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dasinh' },
|
||||
{ oid => '2466', descr => 'inverse hyperbolic cosine',
|
||||
proname => 'acosh', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'dacosh' },
|
||||
{ oid => '2467', descr => 'inverse hyperbolic tangent',
|
||||
proname => 'atanh', prorettype => 'float8', proargtypes => 'float8',
|
||||
prosrc => 'datanh' },
|
||||
|
||||
{ oid => '1618',
|
||||
proname => 'interval_mul', prorettype => 'interval',
|
||||
proargtypes => 'interval float8', prosrc => 'interval_mul' },
|
||||
@ -4201,6 +4223,9 @@
|
||||
{ oid => '1741', descr => 'base 10 logarithm',
|
||||
proname => 'log', prolang => 'sql', prorettype => 'numeric',
|
||||
proargtypes => 'numeric', prosrc => 'select pg_catalog.log(10, $1)' },
|
||||
{ oid => '1481', descr => 'base 10 logarithm',
|
||||
proname => 'log10', prolang => 'sql', prorettype => 'numeric',
|
||||
proargtypes => 'numeric', prosrc => 'select pg_catalog.log(10, $1)' },
|
||||
{ oid => '1742', descr => 'convert float4 to numeric',
|
||||
proname => 'numeric', prorettype => 'numeric', proargtypes => 'float4',
|
||||
prosrc => 'float4_numeric' },
|
||||
|
@ -454,6 +454,43 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
|
||||
| -1.2345678901234e-200
|
||||
(5 rows)
|
||||
|
||||
-- hyperbolic functions
|
||||
SELECT sinh(float8 '0');
|
||||
sinh
|
||||
------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT cosh(float8 '0');
|
||||
cosh
|
||||
------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT tanh(float8 '0');
|
||||
tanh
|
||||
------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT asinh(float8 '0');
|
||||
asinh
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT acosh(float8 '1');
|
||||
acosh
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT atanh(float8 '0');
|
||||
atanh
|
||||
-------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
RESET extra_float_digits;
|
||||
-- test for over- and underflow
|
||||
INSERT INTO FLOAT8_TBL(f1) VALUES ('10e400');
|
||||
|
@ -154,6 +154,14 @@ SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f;
|
||||
|
||||
SELECT '' AS five, * FROM FLOAT8_TBL;
|
||||
|
||||
-- hyperbolic functions
|
||||
SELECT sinh(float8 '0');
|
||||
SELECT cosh(float8 '0');
|
||||
SELECT tanh(float8 '0');
|
||||
SELECT asinh(float8 '0');
|
||||
SELECT acosh(float8 '1');
|
||||
SELECT atanh(float8 '0');
|
||||
|
||||
RESET extra_float_digits;
|
||||
|
||||
-- test for over- and underflow
|
||||
|
Loading…
x
Reference in New Issue
Block a user