472 lines
14 KiB
C
472 lines
14 KiB
C
/*
|
|
* erf function: Copyright (c) 2006 John Maddock
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Replacements for frequently missing libm functions
|
|
*/
|
|
|
|
#ifndef AVUTIL_LIBM_H
|
|
#define AVUTIL_LIBM_H
|
|
|
|
#include <math.h>
|
|
#include "config.h"
|
|
#include "attributes.h"
|
|
#include "intfloat.h"
|
|
#include "mathematics.h"
|
|
|
|
#if HAVE_MIPSFPU && HAVE_INLINE_ASM
|
|
#include "libavutil/mips/libm_mips.h"
|
|
#endif /* HAVE_MIPSFPU && HAVE_INLINE_ASM*/
|
|
|
|
#if !HAVE_ATANF
|
|
#undef atanf
|
|
#define atanf(x) ((float)atan(x))
|
|
#endif /* HAVE_ATANF */
|
|
|
|
#if !HAVE_ATAN2F
|
|
#undef atan2f
|
|
#define atan2f(y, x) ((float)atan2(y, x))
|
|
#endif /* HAVE_ATAN2F */
|
|
|
|
#if !HAVE_POWF
|
|
#undef powf
|
|
#define powf(x, y) ((float)pow(x, y))
|
|
#endif /* HAVE_POWF */
|
|
|
|
#if !HAVE_CBRT
|
|
static av_always_inline double cbrt(double x)
|
|
{
|
|
return x < 0 ? -pow(-x, 1.0 / 3.0) : pow(x, 1.0 / 3.0);
|
|
}
|
|
#endif /* HAVE_CBRT */
|
|
|
|
#if !HAVE_CBRTF
|
|
static av_always_inline float cbrtf(float x)
|
|
{
|
|
return x < 0 ? -powf(-x, 1.0 / 3.0) : powf(x, 1.0 / 3.0);
|
|
}
|
|
#endif /* HAVE_CBRTF */
|
|
|
|
#if !HAVE_COPYSIGN
|
|
static av_always_inline double copysign(double x, double y)
|
|
{
|
|
uint64_t vx = av_double2int(x);
|
|
uint64_t vy = av_double2int(y);
|
|
return av_int2double((vx & UINT64_C(0x7fffffffffffffff)) | (vy & UINT64_C(0x8000000000000000)));
|
|
}
|
|
#endif /* HAVE_COPYSIGN */
|
|
|
|
#if !HAVE_COSF
|
|
#undef cosf
|
|
#define cosf(x) ((float)cos(x))
|
|
#endif /* HAVE_COSF */
|
|
|
|
#if !HAVE_ERF
|
|
static inline double ff_eval_poly(const double *coeff, int size, double x) {
|
|
double sum = coeff[size-1];
|
|
int i;
|
|
for (i = size-2; i >= 0; --i) {
|
|
sum *= x;
|
|
sum += coeff[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
/**
|
|
* erf function
|
|
* Algorithm taken from the Boost project, source:
|
|
* http://www.boost.org/doc/libs/1_46_1/boost/math/special_functions/erf.hpp
|
|
* Use, modification and distribution are subject to the
|
|
* Boost Software License, Version 1.0 (see notice below).
|
|
* Boost Software License - Version 1.0 - August 17th, 2003
|
|
Permission is hereby granted, free of charge, to any person or organization
|
|
obtaining a copy of the software and accompanying documentation covered by
|
|
this license (the "Software") to use, reproduce, display, distribute,
|
|
execute, and transmit the Software, and to prepare derivative works of the
|
|
Software, and to permit third-parties to whom the Software is furnished to
|
|
do so, all subject to the following:
|
|
|
|
The copyright notices in the Software and this entire statement, including
|
|
the above license grant, this restriction and the following disclaimer,
|
|
must be included in all copies of the Software, in whole or in part, and
|
|
all derivative works of the Software, unless such copies or derivative
|
|
works are solely in the form of machine-executable object code generated by
|
|
a source language processor.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
static inline double erf(double z)
|
|
{
|
|
#ifndef FF_ARRAY_ELEMS
|
|
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
|
#endif
|
|
double result;
|
|
|
|
/* handle the symmetry: erf(-x) = -erf(x) */
|
|
if (z < 0)
|
|
return -erf(-z);
|
|
|
|
/* branch based on range of z, and pick appropriate approximation */
|
|
if (z == 0)
|
|
return 0;
|
|
else if (z < 1e-10)
|
|
return z * 1.125 + z * 0.003379167095512573896158903121545171688;
|
|
else if (z < 0.5) {
|
|
// Maximum Deviation Found: 1.561e-17
|
|
// Expected Error Term: 1.561e-17
|
|
// Maximum Relative Change in Control Points: 1.155e-04
|
|
// Max Error found at double precision = 2.961182e-17
|
|
|
|
static const double y = 1.044948577880859375;
|
|
static const double p[] = {
|
|
0.0834305892146531832907,
|
|
-0.338165134459360935041,
|
|
-0.0509990735146777432841,
|
|
-0.00772758345802133288487,
|
|
-0.000322780120964605683831,
|
|
};
|
|
static const double q[] = {
|
|
1,
|
|
0.455004033050794024546,
|
|
0.0875222600142252549554,
|
|
0.00858571925074406212772,
|
|
0.000370900071787748000569,
|
|
};
|
|
double zz = z * z;
|
|
return z * (y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), zz) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), zz));
|
|
}
|
|
/* here onwards compute erfc */
|
|
else if (z < 1.5) {
|
|
// Maximum Deviation Found: 3.702e-17
|
|
// Expected Error Term: 3.702e-17
|
|
// Maximum Relative Change in Control Points: 2.845e-04
|
|
// Max Error found at double precision = 4.841816e-17
|
|
static const double y = 0.405935764312744140625;
|
|
static const double p[] = {
|
|
-0.098090592216281240205,
|
|
0.178114665841120341155,
|
|
0.191003695796775433986,
|
|
0.0888900368967884466578,
|
|
0.0195049001251218801359,
|
|
0.00180424538297014223957,
|
|
};
|
|
static const double q[] = {
|
|
1,
|
|
1.84759070983002217845,
|
|
1.42628004845511324508,
|
|
0.578052804889902404909,
|
|
0.12385097467900864233,
|
|
0.0113385233577001411017,
|
|
0.337511472483094676155e-5,
|
|
};
|
|
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), z - 0.5) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), z - 0.5);
|
|
result *= exp(-z * z) / z;
|
|
return 1 - result;
|
|
}
|
|
else if (z < 2.5) {
|
|
// Max Error found at double precision = 6.599585e-18
|
|
// Maximum Deviation Found: 3.909e-18
|
|
// Expected Error Term: 3.909e-18
|
|
// Maximum Relative Change in Control Points: 9.886e-05
|
|
static const double y = 0.50672817230224609375;
|
|
static const double p[] = {
|
|
-0.0243500476207698441272,
|
|
0.0386540375035707201728,
|
|
0.04394818964209516296,
|
|
0.0175679436311802092299,
|
|
0.00323962406290842133584,
|
|
0.000235839115596880717416,
|
|
};
|
|
static const double q[] = {
|
|
1,
|
|
1.53991494948552447182,
|
|
0.982403709157920235114,
|
|
0.325732924782444448493,
|
|
0.0563921837420478160373,
|
|
0.00410369723978904575884,
|
|
};
|
|
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), z - 1.5) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), z - 1.5);
|
|
result *= exp(-z * z) / z;
|
|
return 1 - result;
|
|
}
|
|
else if (z < 4.5) {
|
|
// Maximum Deviation Found: 1.512e-17
|
|
// Expected Error Term: 1.512e-17
|
|
// Maximum Relative Change in Control Points: 2.222e-04
|
|
// Max Error found at double precision = 2.062515e-17
|
|
static const double y = 0.5405750274658203125;
|
|
static const double p[] = {
|
|
0.00295276716530971662634,
|
|
0.0137384425896355332126,
|
|
0.00840807615555585383007,
|
|
0.00212825620914618649141,
|
|
0.000250269961544794627958,
|
|
0.113212406648847561139e-4,
|
|
};
|
|
static const double q[] = {
|
|
1,
|
|
1.04217814166938418171,
|
|
0.442597659481563127003,
|
|
0.0958492726301061423444,
|
|
0.0105982906484876531489,
|
|
0.000479411269521714493907,
|
|
};
|
|
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), z - 3.5) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), z - 3.5);
|
|
result *= exp(-z * z) / z;
|
|
return 1 - result;
|
|
}
|
|
/* differ from Boost here, the claim of underflow of erfc(x) past 5.8 is
|
|
* slightly incorrect, change to 5.92
|
|
* (really somewhere between 5.9125 and 5.925 is when it saturates) */
|
|
else if (z < 5.92) {
|
|
// Max Error found at double precision = 2.997958e-17
|
|
// Maximum Deviation Found: 2.860e-17
|
|
// Expected Error Term: 2.859e-17
|
|
// Maximum Relative Change in Control Points: 1.357e-05
|
|
static const double y = 0.5579090118408203125;
|
|
static const double p[] = {
|
|
0.00628057170626964891937,
|
|
0.0175389834052493308818,
|
|
-0.212652252872804219852,
|
|
-0.687717681153649930619,
|
|
-2.5518551727311523996,
|
|
-3.22729451764143718517,
|
|
-2.8175401114513378771,
|
|
};
|
|
static const double q[] = {
|
|
1,
|
|
2.79257750980575282228,
|
|
11.0567237927800161565,
|
|
15.930646027911794143,
|
|
22.9367376522880577224,
|
|
13.5064170191802889145,
|
|
5.48409182238641741584,
|
|
};
|
|
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), 1 / z) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), 1 / z);
|
|
result *= exp(-z * z) / z;
|
|
return 1 - result;
|
|
}
|
|
/* handle the nan case, but don't use isnan for max portability */
|
|
else if (z != z)
|
|
return z;
|
|
/* finally return saturated result */
|
|
else
|
|
return 1;
|
|
}
|
|
#endif /* HAVE_ERF */
|
|
|
|
#if !HAVE_EXPF
|
|
#undef expf
|
|
#define expf(x) ((float)exp(x))
|
|
#endif /* HAVE_EXPF */
|
|
|
|
#if !HAVE_EXP2
|
|
#undef exp2
|
|
#define exp2(x) exp((x) * M_LN2)
|
|
#endif /* HAVE_EXP2 */
|
|
|
|
#if !HAVE_EXP2F
|
|
#undef exp2f
|
|
#define exp2f(x) ((float)exp2(x))
|
|
#endif /* HAVE_EXP2F */
|
|
|
|
#if !HAVE_ISINF
|
|
#undef isinf
|
|
/* Note: these do not follow the BSD/Apple/GNU convention of returning -1 for
|
|
-Inf, +1 for Inf, 0 otherwise, but merely follow the POSIX/ISO mandated spec of
|
|
returning a non-zero value for +/-Inf, 0 otherwise. */
|
|
static av_always_inline av_const int avpriv_isinff(float x)
|
|
{
|
|
uint32_t v = av_float2int(x);
|
|
if ((v & 0x7f800000) != 0x7f800000)
|
|
return 0;
|
|
return !(v & 0x007fffff);
|
|
}
|
|
|
|
static av_always_inline av_const int avpriv_isinf(double x)
|
|
{
|
|
uint64_t v = av_double2int(x);
|
|
if ((v & 0x7ff0000000000000) != 0x7ff0000000000000)
|
|
return 0;
|
|
return !(v & 0x000fffffffffffff);
|
|
}
|
|
|
|
#define isinf(x) \
|
|
(sizeof(x) == sizeof(float) \
|
|
? avpriv_isinff(x) \
|
|
: avpriv_isinf(x))
|
|
#endif /* HAVE_ISINF */
|
|
|
|
#if !HAVE_ISNAN
|
|
static av_always_inline av_const int avpriv_isnanf(float x)
|
|
{
|
|
uint32_t v = av_float2int(x);
|
|
if ((v & 0x7f800000) != 0x7f800000)
|
|
return 0;
|
|
return v & 0x007fffff;
|
|
}
|
|
|
|
static av_always_inline av_const int avpriv_isnan(double x)
|
|
{
|
|
uint64_t v = av_double2int(x);
|
|
if ((v & 0x7ff0000000000000) != 0x7ff0000000000000)
|
|
return 0;
|
|
return (v & 0x000fffffffffffff) && 1;
|
|
}
|
|
|
|
#define isnan(x) \
|
|
(sizeof(x) == sizeof(float) \
|
|
? avpriv_isnanf(x) \
|
|
: avpriv_isnan(x))
|
|
#endif /* HAVE_ISNAN */
|
|
|
|
#if !HAVE_ISFINITE
|
|
static av_always_inline av_const int avpriv_isfinitef(float x)
|
|
{
|
|
uint32_t v = av_float2int(x);
|
|
return (v & 0x7f800000) != 0x7f800000;
|
|
}
|
|
|
|
static av_always_inline av_const int avpriv_isfinite(double x)
|
|
{
|
|
uint64_t v = av_double2int(x);
|
|
return (v & 0x7ff0000000000000) != 0x7ff0000000000000;
|
|
}
|
|
|
|
#define isfinite(x) \
|
|
(sizeof(x) == sizeof(float) \
|
|
? avpriv_isfinitef(x) \
|
|
: avpriv_isfinite(x))
|
|
#endif /* HAVE_ISFINITE */
|
|
|
|
#if !HAVE_HYPOT
|
|
static inline av_const double hypot(double x, double y)
|
|
{
|
|
double ret, temp;
|
|
x = fabs(x);
|
|
y = fabs(y);
|
|
|
|
if (isinf(x) || isinf(y))
|
|
return av_int2double(0x7ff0000000000000);
|
|
if (x == 0 || y == 0)
|
|
return x + y;
|
|
if (x < y) {
|
|
temp = x;
|
|
x = y;
|
|
y = temp;
|
|
}
|
|
|
|
y = y/x;
|
|
return x*sqrt(1 + y*y);
|
|
}
|
|
#endif /* HAVE_HYPOT */
|
|
|
|
#if !HAVE_LDEXPF
|
|
#undef ldexpf
|
|
#define ldexpf(x, exp) ((float)ldexp(x, exp))
|
|
#endif /* HAVE_LDEXPF */
|
|
|
|
#if !HAVE_LLRINT
|
|
#undef llrint
|
|
#define llrint(x) ((long long)rint(x))
|
|
#endif /* HAVE_LLRINT */
|
|
|
|
#if !HAVE_LLRINTF
|
|
#undef llrintf
|
|
#define llrintf(x) ((long long)rint(x))
|
|
#endif /* HAVE_LLRINT */
|
|
|
|
#if !HAVE_LOG2
|
|
#undef log2
|
|
#define log2(x) (log(x) * 1.44269504088896340736)
|
|
#endif /* HAVE_LOG2 */
|
|
|
|
#if !HAVE_LOG2F
|
|
#undef log2f
|
|
#define log2f(x) ((float)log2(x))
|
|
#endif /* HAVE_LOG2F */
|
|
|
|
#if !HAVE_LOG10F
|
|
#undef log10f
|
|
#define log10f(x) ((float)log10(x))
|
|
#endif /* HAVE_LOG10F */
|
|
|
|
#if !HAVE_SINF
|
|
#undef sinf
|
|
#define sinf(x) ((float)sin(x))
|
|
#endif /* HAVE_SINF */
|
|
|
|
#if !HAVE_RINT
|
|
static inline double rint(double x)
|
|
{
|
|
return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5);
|
|
}
|
|
#endif /* HAVE_RINT */
|
|
|
|
#if !HAVE_LRINT
|
|
static av_always_inline av_const long int lrint(double x)
|
|
{
|
|
return rint(x);
|
|
}
|
|
#endif /* HAVE_LRINT */
|
|
|
|
#if !HAVE_LRINTF
|
|
static av_always_inline av_const long int lrintf(float x)
|
|
{
|
|
return (int)(rint(x));
|
|
}
|
|
#endif /* HAVE_LRINTF */
|
|
|
|
#if !HAVE_ROUND
|
|
static av_always_inline av_const double round(double x)
|
|
{
|
|
return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);
|
|
}
|
|
#endif /* HAVE_ROUND */
|
|
|
|
#if !HAVE_ROUNDF
|
|
static av_always_inline av_const float roundf(float x)
|
|
{
|
|
return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);
|
|
}
|
|
#endif /* HAVE_ROUNDF */
|
|
|
|
#if !HAVE_TRUNC
|
|
static av_always_inline av_const double trunc(double x)
|
|
{
|
|
return (x > 0) ? floor(x) : ceil(x);
|
|
}
|
|
#endif /* HAVE_TRUNC */
|
|
|
|
#if !HAVE_TRUNCF
|
|
static av_always_inline av_const float truncf(float x)
|
|
{
|
|
return (x > 0) ? floor(x) : ceil(x);
|
|
}
|
|
#endif /* HAVE_TRUNCF */
|
|
|
|
#endif /* AVUTIL_LIBM_H */
|