Hi Andrew and Tom, I considered that option before writing my patch but I refrained for 2 reasons:
- There is no consensus about how to name these functions. The standard 8000-2 goes with arsinh, arcosh and artanh, but you will find easily arcsinh, arccosh and arctanh or even argsinh, argcosh and argtanh. In IT, the names asinh, acosh and atanh are commonly used too. We might implement them with asinh, acosh and atanh names and add aliases if SQL standard decide to add it under other names though. - If we go with inverse hyperbolic functions, I guess we could add other hyperbolic functions as hyperbolic cosecant, secant and cotangent too. Then it adds the inverse hyperbolic functions of these three functions. These six functions are not described in math.h library. I guess it's because these functions are quite simple to deduce from the others. So, as you're asking that too, maybe my reasons weren't good enough. You'll find enclosed a new version of the patch with asinh, acosh and atanh (v5). Then I tried for several days to implement the 6 last hyperbolic functions, but I wasn't satisfied with the result, so I just dropped it. Cheers, Lætitia Le dim. 3 févr. 2019 à 16:12, Tom Lane <t...@sss.pgh.pa.us> a écrit : > Andrew Gierth <and...@tao11.riddles.org.uk> writes: > > The spec doesn't require the inverse functions (asinh, acosh, atanh), > > but surely there is no principled reason to omit them? > > +1 --- AFAICS, the C library has offered all six since C89. > > regards, tom lane > -- *Think! Do you really need to print this email ? * *There is no Planet B.*
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4930ec17f6..61980cfcf5 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -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,7 +1160,7 @@ </para> <para> - Finally, <xref linkend="functions-math-trig-table"/> shows the + <xref linkend="functions-math-trig-table"/> shows the available trigonometric functions. All trigonometric functions take arguments and return values of type <type>double precision</type>. Each of the trigonometric functions comes in @@ -1311,8 +1324,103 @@ </para> </note> - </sect1> + <para> + Finally, <xref linkend="functions-math-hyp-table"/> shows the + available hyperbolic functions. All hyperbolic functions apply to + hyperbolic angles expressed in hyperbolic radians. All inverse hyperbolic + functions applies to the area of a hyperbolic angle. + </para> + + <table id="functions-math-hyp-table"> + <title>Hyperbolic Functions</title> + <tgroup cols="5"> + <thead> + <row> + <entry>Function</entry> + <entry>Return Type</entry> + <entry>Description</entry> + <entry>Example</entry> + <entry>Result</entry> + </row> + </thead> + <tbody> + <row> + <entry> + <indexterm> + <primary>sinh</primary> + </indexterm> + <literal><function>sinh(dp)</function></literal> + </entry> + <entry><type>dp</type></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(dp)</function></literal> + </entry> + <entry><type>dp</type></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(dp)</function></literal> + </entry> + <entry><type>dp</type></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(dp)</function></literal> + </entry> + <entry><type>dp</type></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(dp)</function></literal> + </entry> + <entry><type>dp</type></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(dp)</function></literal> + </entry> + <entry><type>dp</type></entry> + <entry>inverse hyperbolic tangent</entry> + <entry><literal>atanh(0)</literal></entry> + <entry><literal>0</literal></entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> <sect1 id="functions-string"> <title>String Functions and Operators</title> diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 117ded8d1d..8a0690e11e 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -2375,6 +2375,169 @@ radians(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(float8_mul(arg1, RADIANS_PER_DEGREE)); } +/* ========== 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, it + * can be either -infinite or infinite, 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 infinite. + */ + 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 have errno check because it nevers 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; + + errno = 0; + result = asinh(arg1); + + /* + * if an ERANGE error occurs, it means there is an overflow. For asinh, it + * can be either -infinite or infinite, 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); +} + +/* + * dacosh - returns the inverse hyperbolic cosine of arg1 + */ +Datum +dacosh(PG_FUNCTION_ARGS) +{ + float8 arg1 = PG_GETARG_FLOAT8(0); + float8 result; + + errno = 0; + result = acosh(arg1); + + /* + * If arg1 is less than 1, a domain error occurs. + */ + if (errno == EDOM) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("input can't be less than 1"))); + + /* + * if an ERANGE error occurs, it means there is an overflow. As acosh is + * always positive, it means the result is positive infinite. + */ + if (errno == ERANGE) + result = get_float8_infinity(); + + 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; + + errno = 0; + result = tanh(arg1); + + /* + * if an ERANGE error occurs, it normaly means there is an overflow, but due + * to a bug in glibc 2.9 and earlier, an EDOM can occur instead of ERANGE. + * In that case, result is either HUGE_VAL or -HUGE_VAL. + */ + if (errno == ERANGE && result != HUGE_VAL && result != -HUGE_VAL) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("input can't be less than -1 or more than 1"))); + else if (errno == EDOM || errno == ERANGE) + { + if (arg1 < 0) + result = -get_float8_infinity(); + else + result = get_float8_infinity(); + } + + check_float8_val(result, true, true); + PG_RETURN_FLOAT8(result); +} /* * drandom - returns a random number diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index b8de13f03b..a2a2296ada 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -2617,6 +2617,9 @@ { oid => '1340', descr => 'base 10 logarithm', proname => 'log', prorettype => 'float8', proargtypes => 'float8', prosrc => 'dlog10' }, +{ oid => '1364', 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' }, @@ -3283,6 +3286,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' }, @@ -4195,6 +4217,9 @@ { oid => '1741', descr => 'base 10 logarithm', proname => 'log', prolang => 'sql', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'select pg_catalog.log(10, $1)' }, +{ oid => '2023', descr => 'base 10 logarithm', + proname => 'log10', prolang => '14', 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' }, diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out index 75c0bf389b..7e1d639d4d 100644 --- a/src/test/regress/expected/float8.out +++ b/src/test/regress/expected/float8.out @@ -632,4 +632,41 @@ FROM (SELECT 10*cosd(a), 10*sind(a) 10 | 0 | 0 | t (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; diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql index 6595fd2b95..152c4fa126 100644 --- a/src/test/regress/sql/float8.sql +++ b/src/test/regress/sql/float8.sql @@ -232,4 +232,12 @@ SELECT x, y, FROM (SELECT 10*cosd(a), 10*sind(a) FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y); +-- 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;