| /* | |
| * 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 */ |