├── .gitignore ├── MANIFEST.in ├── README.MD ├── clean.sh ├── lets_be_rational.egg-info ├── PKG-INFO ├── SOURCES.txt ├── dependency_links.txt └── top_level.txt ├── lets_be_rational ├── LetsBeRational.py └── __init__.py ├── make.bat ├── make_linux.sh ├── make_osx.sh ├── setup.py └── src ├── LetsBeRational.cpp ├── LetsBeRational.i ├── LetsBeRational.py ├── erf_cody.cpp ├── importexport.h ├── normaldistribution.cpp ├── normaldistribution.h ├── rationalcubic.cpp ├── rationalcubic.h └── version.h /.gitignore: -------------------------------------------------------------------------------- 1 | # lets_be_rational/*.py 2 | lets_be_rational/*.so 3 | !lets_be_rational/__init__.py 4 | src/LetsBeRational_wrap.c 5 | *.o 6 | *.pyc 7 | 8 | build 9 | # dist 10 | # *.egg-info 11 | # *.egg 12 | 13 | *scratch* 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include src/* 2 | include README* 3 | 4 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | # ```lets_be_rational``` 3 | 4 | A Python SWIG wrapper for the functions in "Let's Be Rational" by Peter Jäckel. 5 | 6 | ## About "Let's be Rational": 7 | 8 | ["Let's Be Rational"](http://www.pjaeckel.webspace.virginmedia.com/LetsBeRational.pdf) is a paper by [Peter Jäckel](http://jaeckel.org) showing *"how Black's volatility can be implied from option prices with as little as two iterations to maximum attainable precision on standard (64 bit floating point) hardware for all possible inputs."* 9 | 10 | The paper is accompanied by the full C source code, which resides at [www.jaeckel.org/LetsBeRational.7z](www.jaeckel.org/LetsBeRational.7z). 11 | 12 | ``` 13 | Copyright © 2013-2014 Peter Jäckel. 14 | 15 | Permission to use, copy, modify, and distribute this software is freely granted, 16 | provided that this notice is preserved. 17 | 18 | WARRANTY DISCLAIMER 19 | The Software is provided "as is" without warranty of any kind, either express or implied, 20 | including without limitation any implied warranties of condition, uninterrupted use, 21 | merchantability, fitness for a particular purpose, or non-infringement. 22 | ``` 23 | 24 | ## Dependencies 25 | 26 | SWIG must be installed prior to installing ```lets_be_rational```. Download and 27 | install [SWIG](http://www.swig.org/download.html). 28 | 29 | 30 | ## Installation with pip 31 | 32 | Pip is the recommended method for installing ```lets_be_rational```. 33 | 34 | Pip can be downloaded at https://pypi.python.org/pypi/pip . 35 | 36 | In the command prompt, simply type the following command: 37 | 38 | pip install lets_be_rational 39 | 40 | ## Compiling from source 41 | 42 | Alternatively, it is possible to install by cloning the ```lets_be_rational``` Github 43 | repository and running the make file for your OS. The make files are in the in the 44 | same directory ```lets_be_rational``` along with the other source code files. 45 | 46 | 47 | ### Windows 48 | 49 | For Windows, run the following: 50 | 51 | ``` 52 | make.bat 53 | ``` 54 | 55 | ### Linux 56 | 57 | For Linux run the following: 58 | 59 | ``` 60 | ./make_linux.sh 61 | ``` 62 | 63 | ### Mac OS X 64 | 65 | For Mac OS X run the following: 66 | 67 | ``` 68 | ./make_osx.sh 69 | ``` 70 | 71 | ### After running the make file 72 | 73 | To make ```lets_be_rational``` available to your Python interpreter or project, 74 | add ```lets_be_rational``` to your Python path. 75 | 76 | 77 | ## Using ```lets_be_rational``` 78 | 79 | ```lets_be_rational``` can be used in Python as follows: 80 | ``` 81 | >>> import lets_be_rational 82 | ``` 83 | 84 | or 85 | 86 | ``` 87 | >>> from lets_be_rational import implied_volatility_from_a_transformed_rational_guess 88 | ``` 89 | etc. 90 | 91 | ### Contents 92 | 93 | The following functions from Peter Jäckel's code are exposed in the Python environment. 94 | For details refer to the paper ["Let's Be Rational"](http://www.pjaeckel.webspace.virginmedia.com/LetsBeRational.pdf) and its accompanying C source code at [www.jaeckel.org/LetsBeRational.7z](www.jaeckel.org/LetsBeRational.7z). 95 | 96 | ``` 97 | black 98 | implied_volatility_from_a_transformed_rational_guess 99 | implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 100 | normalised_black 101 | normalised_black_call 102 | normalised_implied_volatility_from_a_transformed_rational_guess 103 | normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 104 | normalised_vega 105 | norm_cdf 106 | ``` 107 | 108 | 109 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | rm *.o 2 | rm src/LetsBeRational_wrap.c 3 | rm lets_be_rational/LetsBeRational.py 4 | rm lets_be_rational/*.pyc 5 | rm lets_be_rational/*.so 6 | rm lets_be_rational/*.pyd 7 | rm -rf build 8 | rm -rf dist 9 | rm -rf *.egg* 10 | -------------------------------------------------------------------------------- /lets_be_rational.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.1 2 | Name: lets-be-rational 3 | Version: 1.0.9 4 | Summary: 5 | Peter Jäckel's LetsBeRational an extremely fast and accurate method for 6 | obtaining Black's implied from option prices with as little as two 7 | iterations to maximum attainable precision on standard 8 | (64 bit floating point) hardware for all possible inputs. 9 | 10 | Home-page: http://vollib.org 11 | Author: vollib 12 | Author-email: support@quantycarlo.com 13 | License: MIT 14 | Download-URL: git+https://github.com/vollib/lets_be_rational.git#egg=lets_be_rational 15 | Description: UNKNOWN 16 | Platform: UNKNOWN 17 | -------------------------------------------------------------------------------- /lets_be_rational.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | MANIFEST.in 2 | ReadMe.txt 3 | setup.py 4 | lets_be_rational/LetsBeRational.py 5 | lets_be_rational/__init__.py 6 | lets_be_rational.egg-info/PKG-INFO 7 | lets_be_rational.egg-info/SOURCES.txt 8 | lets_be_rational.egg-info/dependency_links.txt 9 | lets_be_rational.egg-info/top_level.txt 10 | src/LetsBeRational.cpp 11 | src/LetsBeRational.i 12 | src/LetsBeRational.py 13 | src/LetsBeRational_wrap.c 14 | src/erf_cody.cpp 15 | src/importexport.h 16 | src/normaldistribution.cpp 17 | src/normaldistribution.h 18 | src/rationalcubic.cpp 19 | src/rationalcubic.h 20 | src/version.h -------------------------------------------------------------------------------- /lets_be_rational.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lets_be_rational.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | lets_be_rational 2 | -------------------------------------------------------------------------------- /lets_be_rational/LetsBeRational.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (http://www.swig.org). 2 | # Version 3.0.5 3 | # 4 | # Do not make changes to this file unless you know what you are doing--modify 5 | # the SWIG interface file instead. 6 | 7 | 8 | 9 | 10 | 11 | from sys import version_info 12 | if version_info >= (2, 6, 0): 13 | def swig_import_helper(): 14 | from os.path import dirname 15 | import imp 16 | fp = None 17 | try: 18 | fp, pathname, description = imp.find_module('_LetsBeRational', [dirname(__file__)]) 19 | except ImportError: 20 | import _LetsBeRational 21 | return _LetsBeRational 22 | if fp is not None: 23 | try: 24 | _mod = imp.load_module('_LetsBeRational', fp, pathname, description) 25 | finally: 26 | fp.close() 27 | return _mod 28 | _LetsBeRational = swig_import_helper() 29 | del swig_import_helper 30 | else: 31 | import _LetsBeRational 32 | del version_info 33 | try: 34 | _swig_property = property 35 | except NameError: 36 | pass # Python < 2.2 doesn't have 'property'. 37 | 38 | 39 | def _swig_setattr_nondynamic(self, class_type, name, value, static=1): 40 | if (name == "thisown"): 41 | return self.this.own(value) 42 | if (name == "this"): 43 | if type(value).__name__ == 'SwigPyObject': 44 | self.__dict__[name] = value 45 | return 46 | method = class_type.__swig_setmethods__.get(name, None) 47 | if method: 48 | return method(self, value) 49 | if (not static): 50 | if _newclass: 51 | object.__setattr__(self, name, value) 52 | else: 53 | self.__dict__[name] = value 54 | else: 55 | raise AttributeError("You cannot add attributes to %s" % self) 56 | 57 | 58 | def _swig_setattr(self, class_type, name, value): 59 | return _swig_setattr_nondynamic(self, class_type, name, value, 0) 60 | 61 | 62 | def _swig_getattr_nondynamic(self, class_type, name, static=1): 63 | if (name == "thisown"): 64 | return self.this.own() 65 | method = class_type.__swig_getmethods__.get(name, None) 66 | if method: 67 | return method(self) 68 | if (not static): 69 | return object.__getattr__(self, name) 70 | else: 71 | raise AttributeError(name) 72 | 73 | def _swig_getattr(self, class_type, name): 74 | return _swig_getattr_nondynamic(self, class_type, name, 0) 75 | 76 | 77 | def _swig_repr(self): 78 | try: 79 | strthis = "proxy of " + self.this.__repr__() 80 | except: 81 | strthis = "" 82 | return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) 83 | 84 | try: 85 | _object = object 86 | _newclass = 1 87 | except AttributeError: 88 | class _object: 89 | pass 90 | _newclass = 0 91 | 92 | 93 | 94 | def norm_cdf(z): 95 | return _LetsBeRational.norm_cdf(z) 96 | norm_cdf = _LetsBeRational.norm_cdf 97 | 98 | def normalised_black_call(x, s): 99 | return _LetsBeRational.normalised_black_call(x, s) 100 | normalised_black_call = _LetsBeRational.normalised_black_call 101 | 102 | def normalised_vega(x, s): 103 | return _LetsBeRational.normalised_vega(x, s) 104 | normalised_vega = _LetsBeRational.normalised_vega 105 | 106 | def normalised_black(x, s, q): 107 | return _LetsBeRational.normalised_black(x, s, q) 108 | normalised_black = _LetsBeRational.normalised_black 109 | 110 | def black(F, K, sigma, T, q): 111 | return _LetsBeRational.black(F, K, sigma, T, q) 112 | black = _LetsBeRational.black 113 | 114 | def implied_volatility_from_a_transformed_rational_guess(price, F, K, T, q): 115 | return _LetsBeRational.implied_volatility_from_a_transformed_rational_guess(price, F, K, T, q) 116 | implied_volatility_from_a_transformed_rational_guess = _LetsBeRational.implied_volatility_from_a_transformed_rational_guess 117 | 118 | def implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(price, F, K, T, q, N): 119 | return _LetsBeRational.implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(price, F, K, T, q, N) 120 | implied_volatility_from_a_transformed_rational_guess_with_limited_iterations = _LetsBeRational.implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 121 | 122 | def normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta, x, q, N): 123 | return _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta, x, q, N) 124 | normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations = _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 125 | 126 | def normalised_implied_volatility_from_a_transformed_rational_guess(beta, x, q): 127 | return _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess(beta, x, q) 128 | normalised_implied_volatility_from_a_transformed_rational_guess = _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess 129 | # This file is compatible with both classic and new-style classes. 130 | 131 | 132 | -------------------------------------------------------------------------------- /lets_be_rational/__init__.py: -------------------------------------------------------------------------------- 1 | from LetsBeRational import black 2 | from LetsBeRational import implied_volatility_from_a_transformed_rational_guess 3 | from LetsBeRational import implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 4 | from LetsBeRational import normalised_black 5 | from LetsBeRational import normalised_black_call 6 | from LetsBeRational import normalised_vega 7 | from LetsBeRational import normalised_implied_volatility_from_a_transformed_rational_guess 8 | from LetsBeRational import normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 9 | from LetsBeRational import norm_cdf 10 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | 2 | swig -python src\LetsBeRational.i 3 | 4 | g++ -c -Wall src\erf_cody.cpp src\rationalcubic.cpp src\normaldistribution.cpp src\LetsBeRational.cpp 5 | 6 | g++ -c -Wall src\LetsBeRational_wrap.c -Ic:\python27\include 7 | 8 | g++ -Wall -shared -I C:\python27\include -I C:\Python27\Lib\site-packages\numpy\core\include\ *.o -o lets_be_rational\_LetsBeRational.pyd -L C:\Python27\libs\ -lpython27 9 | 10 | del *.o 11 | -------------------------------------------------------------------------------- /make_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | swig -python -outdir lets_be_rational src/LetsBeRational.i 3 | gcc -c src/erf_cody.cpp src/rationalcubic.cpp src/normaldistribution.cpp src/LetsBeRational.cpp src/LetsBeRational_wrap.c -I/usr/include/python2.7 4 | ld -shared *.o -o lets_be_rational/_LetsBeRational.so 5 | rm *.o 6 | -------------------------------------------------------------------------------- /make_osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | swig -python -outdir lets_be_rational src/LetsBeRational.i 3 | gcc -c -arch x86_64 -fPIC src/erf_cody.cpp src/rationalcubic.cpp src/normaldistribution.cpp src/LetsBeRational.cpp src/LetsBeRational_wrap.c -I/usr/include/python2.7 4 | ld -bundle -macosx_version_min 10.7 -flat_namespace -undefined suppress -o lets_be_rational/_LetsBeRational.so *.o 5 | rm *.o 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | from setuptools import setup, Extension 5 | 6 | 7 | cpp_module = Extension('lets_be_rational._LetsBeRational', 8 | sources = [ 9 | 'src/erf_cody.cpp', 10 | 'src/normaldistribution.cpp', 11 | 'src/rationalcubic.cpp', 12 | 'src/LetsBeRational.cpp', 13 | 'src/LetsBeRational.i' 14 | ], 15 | depends=[ 16 | 'src/importexport.h', 17 | 'src/normaldistribution.h', 18 | 'src/rationalcubic.h', 19 | 'src/version.h' 20 | ], 21 | swig_opts=['-outdir', 'lets_be_rational'], 22 | # extra_link_args=['-flat_namespace'] 23 | ) 24 | 25 | setup( 26 | name='lets_be_rational', 27 | version='1.0.9', 28 | description=''' 29 | Peter Jäckel's LetsBeRational an extremely fast and accurate method for 30 | obtaining Black's implied volatility from option prices with as little as two 31 | iterations to maximum attainable precision on standard 32 | (64 bit floating point) hardware for all possible inputs. 33 | ''', 34 | url='http://jaeckel.org', 35 | download_url='git+https://github.com/vollib/lets_be_rational.git#egg=lets_be_rational', 36 | maintainer='vollib', 37 | maintainer_email='support@quantycarlo.com', 38 | license="MIT", 39 | ext_modules=[cpp_module], 40 | packages=['lets_be_rational'], 41 | data_files=[ 42 | ('src', [ 43 | 'src/importexport.h', 44 | 'src/normaldistribution.h', 45 | 'src/rationalcubic.h', 46 | 'src/version.h' 47 | ]),] 48 | ) 49 | -------------------------------------------------------------------------------- /src/LetsBeRational.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // This source code resides at www.jaeckel.org/LetsBeRational.7z . 3 | // 4 | // ====================================================================================== 5 | // Copyright © 2013-2014 Peter Jäckel. 6 | // 7 | // Permission to use, copy, modify, and distribute this software is freely granted, 8 | // provided that this notice is preserved. 9 | // 10 | // WARRANTY DISCLAIMER 11 | // The Software is provided "as is" without warranty of any kind, either express or implied, 12 | // including without limitation any implied warranties of condition, uninterrupted use, 13 | // merchantability, fitness for a particular purpose, or non-infringement. 14 | // ====================================================================================== 15 | // 16 | 17 | //#include "lets_be_rational.h" 18 | 19 | // To cross-compile on a command line, you could just use something like 20 | // 21 | // i686-w64-mingw32-g++ -w -fpermissive -shared -DNDEBUG -Ofast erf_cody.cpp rationalcubic.cpp normaldistribution.cpp lets_be_rational.cpp xlcall.cpp excel_registration.cpp xlcall32.lib -o lets_be_rational.xll -static-libstdc++ -static-libgcc -s 22 | // 23 | // To compile into a shared library on non-Windows system, you could try 24 | // 25 | // g++ -fPIC -shared -DNDEBUG -Ofast erf_cody.cpp rationalcubic.cpp normaldistribution.cpp lets_be_rational.cpp -o lets_be_rational.so 26 | // 27 | 28 | #if defined(_MSC_VER) 29 | # define NOMINMAX // to suppress MSVC's definitions of min() and max() 30 | // These four pragmas are the equivalent to /fp:fast. 31 | # pragma float_control( except, off ) 32 | # pragma float_control( precise, off ) 33 | # pragma fp_contract( on ) 34 | # pragma fenv_access( off ) 35 | #endif 36 | 37 | #include "normaldistribution.h" 38 | #include "rationalcubic.h" 39 | #include 40 | #include 41 | #include 42 | #if defined(_WIN32) || defined(_WIN64) 43 | # include 44 | #endif 45 | 46 | #define TWO_PI 6.283185307179586476925286766559005768394338798750 47 | #define SQRT_PI_OVER_TWO 1.253314137315500251207882642405522626503493370305 // sqrt(pi/2) to avoid misinterpretation. 48 | #define SQRT_THREE 1.732050807568877293527446341505872366942805253810 49 | #define SQRT_ONE_OVER_THREE 0.577350269189625764509148780501957455647601751270 50 | #define TWO_PI_OVER_SQRT_TWENTY_SEVEN 1.209199576156145233729385505094770488189377498728 // 2*pi/sqrt(27) 51 | #define PI_OVER_SIX 0.523598775598298873077107230546583814032861566563 52 | 53 | namespace { 54 | static const double SQRT_DBL_EPSILON = sqrt(DBL_EPSILON); 55 | static const double FOURTH_ROOT_DBL_EPSILON = sqrt(SQRT_DBL_EPSILON); 56 | static const double EIGHTH_ROOT_DBL_EPSILON = sqrt(FOURTH_ROOT_DBL_EPSILON); 57 | static const double SIXTEENTH_ROOT_DBL_EPSILON = sqrt(EIGHTH_ROOT_DBL_EPSILON); 58 | static const double SQRT_DBL_MIN = sqrt(DBL_MIN); 59 | static const double SQRT_DBL_MAX = sqrt(DBL_MAX); 60 | 61 | // Set this to 0 if you want positive results for (positive) denormalized inputs, else to DBL_MIN. 62 | // Note that you cannot achieve full machine accuracy from denormalized inputs! 63 | static const double DENORMALIZATION_CUTOFF = 0; 64 | 65 | static const double VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_BELOW_INTRINSIC = -DBL_MAX; 66 | static const double VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM = DBL_MAX; 67 | 68 | inline bool is_below_horizon(double x){ return fabs(x) < DENORMALIZATION_CUTOFF; } // This weeds out denormalized (a.k.a. 'subnormal') numbers. 69 | 70 | // See https://www.kernel.org/doc/Documentation/atomic_ops.txt for further details on this simplistic implementation of an atomic flag that is *not* volatile. 71 | typedef struct { 72 | #if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) 73 | long data; 74 | #else 75 | int data; 76 | #endif 77 | } atomic_t; 78 | 79 | static atomic_t implied_volatility_maximum_iterations = { 2 }; // (DBL_DIG*20)/3 ≈ 100 . Only needed when the iteration effectively alternates Householder/Halley/Newton steps and binary nesting due to roundoff truncation. 80 | 81 | #ifdef ENABLE_SWITCHING_THE_OUTPUT_TO_ITERATION_COUNT 82 | static atomic_t implied_volatility_output_type = { 0 }; 83 | inline double implied_volatility_output(int count, double volatility){ return implied_volatility_output_type.data>0 ? count : volatility; } 84 | #else 85 | inline double implied_volatility_output(int count, double volatility){ return volatility; } 86 | #endif 87 | 88 | #ifdef ENABLE_CHANGING_THE_HOUSEHOLDER_METHOD_ORDER 89 | static atomic_t implied_volatility_householder_method_order = { 4 }; 90 | inline double householder_factor(double newton, double halley, double hh3){ 91 | return implied_volatility_householder_method_order.data > 3 ? (1+0.5*halley*newton)/(1+newton*(halley+hh3*newton/6)) : ( implied_volatility_householder_method_order.data > 2 ? 1/(1+0.5*halley*newton) : 1 ); 92 | } 93 | #else 94 | inline double householder_factor(double newton, double halley, double hh3){ return (1+0.5*halley*newton)/(1+newton*(halley+hh3*newton/6)); } 95 | #endif 96 | 97 | } 98 | double normalised_intrinsic(double x, double q /* q=±1 */){ 99 | if (q*x<=0) 100 | return 0; 101 | const double x2=x*x; 102 | if (x2<98*FOURTH_ROOT_DBL_EPSILON ) // The factor 98 is computed from last coefficient: √√92897280 = 98.1749 103 | return fabs( std::max( (q<0?-1:1)*x*(1+x2*((1.0/24.0)+x2*((1.0/1920.0)+x2*((1.0/322560.0)+(1.0/92897280.0)*x2)))) , 0.0 ) ); 104 | const double b_max = exp(0.5*x), one_over_b_max = 1 / b_max; 105 | return fabs(std::max((q<0?-1:1)*(b_max-one_over_b_max),0.)); 106 | } 107 | 108 | double normalised_intrinsic_call(double x){ return normalised_intrinsic(x,1); } 109 | 110 | // Asymptotic expansion of 111 | // 112 | // b = Φ(h+t)·exp(x/2) - Φ(h-t)·exp(-x/2) 113 | // with 114 | // h = x/s and t = s/2 115 | // which makes 116 | // b = Φ(h+t)·exp(h·t) - Φ(h-t)·exp(-h·t) 117 | // 118 | // exp(-(h²+t²)/2) 119 | // = --------------- · [ Y(h+t) - Y(h-t) ] 120 | // √(2π) 121 | // with 122 | // Y(z) := Φ(z)/φ(z) 123 | // 124 | // for large negative (t-|h|) by the aid of Abramowitz & Stegun (26.2.12) where Φ(z) = φ(z)/|z|·[1-1/z^2+...]. 125 | // We define 126 | // r 127 | // A(h,t) := --- · [ Y(h+t) - Y(h-t) ] 128 | // t 129 | // 130 | // with r := (h+t)·(h-t) and give an expansion for A(h,t) in q:=(h/r)² expressed in terms of e:=(t/h)² . 131 | double asymptotic_expansion_of_normalized_black_call(double h, double t){ 132 | const double e=(t/h)*(t/h), r=((h+t)*(h-t)), q=(h/r)*(h/r); 133 | // 17th order asymptotic expansion of A(h,t) in q, sufficient for Φ(h) [and thus y(h)] to have relative accuracy of 1.64E-16 for h <= η with η:=-10. 134 | const double asymptotic_expansion_sum = (2.0+q*(-6.0E0-2.0*e+3.0*q*(1.0E1+e*(2.0E1+2.0*e)+5.0*q*(-1.4E1+e*(-7.0E1+e*(-4.2E1-2.0*e))+7.0*q*(1.8E1+e*(1.68E2+e*(2.52E2+e*(7.2E1+2.0*e)))+9.0*q*(-2.2E1+e*(-3.3E2+e*(-9.24E2+e*(-6.6E2+e*(-1.1E2-2.0*e))))+1.1E1*q*(2.6E1+e*(5.72E2+e*(2.574E3+e*(3.432E3+e*(1.43E3+e*(1.56E2+2.0*e)))))+1.3E1*q*(-3.0E1+e*(-9.1E2+e*(-6.006E3+e*(-1.287E4+e*(-1.001E4+e*(-2.73E3+e*(-2.1E2-2.0*e))))))+1.5E1*q*(3.4E1+e*(1.36E3+e*(1.2376E4+e*(3.8896E4+e*(4.862E4+e*(2.4752E4+e*(4.76E3+e*(2.72E2+2.0*e)))))))+1.7E1*q*(-3.8E1+e*(-1.938E3+e*(-2.3256E4+e*(-1.00776E5+e*(-1.84756E5+e*(-1.51164E5+e*(-5.4264E4+e*(-7.752E3+e*(-3.42E2-2.0*e))))))))+1.9E1*q*(4.2E1+e*(2.66E3+e*(4.0698E4+e*(2.3256E5+e*(5.8786E5+e*(7.05432E5+e*(4.0698E5+e*(1.08528E5+e*(1.197E4+e*(4.2E2+2.0*e)))))))))+2.1E1*q*(-4.6E1+e*(-3.542E3+e*(-6.7298E4+e*(-4.90314E5+e*(-1.63438E6+e*(-2.704156E6+e*(-2.288132E6+e*(-9.80628E5+e*(-2.01894E5+e*(-1.771E4+e*(-5.06E2-2.0*e))))))))))+2.3E1*q*(5.0E1+e*(4.6E3+e*(1.0626E5+e*(9.614E5+e*(4.08595E6+e*(8.9148E6+e*(1.04006E7+e*(6.53752E6+e*(2.16315E6+e*(3.542E5+e*(2.53E4+e*(6.0E2+2.0*e)))))))))))+2.5E1*q*(-5.4E1+e*(-5.85E3+e*(-1.6146E5+e*(-1.77606E6+e*(-9.37365E6+e*(-2.607579E7+e*(-4.01166E7+e*(-3.476772E7+e*(-1.687257E7+e*(-4.44015E6+e*(-5.9202E5+e*(-3.51E4+e*(-7.02E2-2.0*e))))))))))))+2.7E1*q*(5.8E1+e*(7.308E3+e*(2.3751E5+e*(3.12156E6+e*(2.003001E7+e*(6.919458E7+e*(1.3572783E8+e*(1.5511752E8+e*(1.0379187E8+e*(4.006002E7+e*(8.58429E6+e*(9.5004E5+e*(4.7502E4+e*(8.12E2+2.0*e)))))))))))))+2.9E1*q*(-6.2E1+e*(-8.99E3+e*(-3.39822E5+e*(-5.25915E6+e*(-4.032015E7+e*(-1.6934463E8+e*(-4.1250615E8+e*(-6.0108039E8+e*(-5.3036505E8+e*(-2.8224105E8+e*(-8.870433E7+e*(-1.577745E7+e*(-1.472562E6+e*(-6.293E4+e*(-9.3E2-2.0*e))))))))))))))+3.1E1*q*(6.6E1+e*(1.0912E4+e*(4.74672E5+e*(8.544096E6+e*(7.71342E7+e*(3.8707344E8+e*(1.14633288E9+e*(2.07431664E9+e*(2.33360622E9+e*(1.6376184E9+e*(7.0963464E8+e*(1.8512208E8+e*(2.7768312E7+e*(2.215136E6+e*(8.184E4+e*(1.056E3+2.0*e)))))))))))))))+3.3E1*(-7.0E1+e*(-1.309E4+e*(-6.49264E5+e*(-1.344904E7+e*(-1.4121492E8+e*(-8.344518E8+e*(-2.9526756E9+e*(-6.49588632E9+e*(-9.0751353E9+e*(-8.1198579E9+e*(-4.6399188E9+e*(-1.6689036E9+e*(-3.67158792E8+e*(-4.707164E7+e*(-3.24632E6+e*(-1.0472E5+e*(-1.19E3-2.0*e)))))))))))))))))*q))))))))))))))))); 135 | const double b = ONE_OVER_SQRT_TWO_PI*exp((-0.5*(h*h+t*t)))*(t/r)*asymptotic_expansion_sum; 136 | return fabs(std::max(b , 0.)); 137 | } 138 | 139 | namespace { /* η */ static const double asymptotic_expansion_accuracy_threshold = -10; } 140 | 141 | double normalised_black_call_using_erfcx(double h, double t) { 142 | // Given h = x/s and t = s/2, the normalised Black function can be written as 143 | // 144 | // b(x,s) = Φ(x/s+s/2)·exp(x/2) - Φ(x/s-s/2)·exp(-x/2) 145 | // = Φ(h+t)·exp(h·t) - Φ(h-t)·exp(-h·t) . (*) 146 | // 147 | // It is mentioned in section 4 (and discussion of figures 2 and 3) of George Marsaglia's article "Evaluating the 148 | // Normal Distribution" (available at http://www.jstatsoft.org/v11/a05/paper) that the error of any cumulative normal 149 | // function Φ(z) is dominated by the hardware (or compiler implementation) accuracy of exp(-z²/2) which is not 150 | // reliably more than 14 digits when z is large. The accuracy of Φ(z) typically starts coming down to 14 digits when 151 | // z is around -8. For the (normalised) Black function, as above in (*), this means that we are subtracting two terms 152 | // that are each products of terms with about 14 digits of accuracy. The net result, in each of the products, is even 153 | // less accuracy, and then we are taking the difference of these terms, resulting in even less accuracy. When we are 154 | // using the asymptotic expansion asymptotic_expansion_of_normalized_black_call() invoked in the second branch at the 155 | // beginning of this function, we are using only *one* exponential instead of 4, and this improves accuracy. It 156 | // actually improves it a bit more than you would expect from the above logic, namely, almost the full two missing 157 | // digits (in 64 bit IEEE floating point). Unfortunately, going higher order in the asymptotic expansion will not 158 | // enable us to gain more accuracy (by extending the range in which we could use the expansion) since the asymptotic 159 | // expansion, being a divergent series, can never gain 16 digits of accuracy for z=-8 or just below. The best you can 160 | // get is about 15 digits (just), for about 35 terms in the series (26.2.12), which would result in an prohibitively 161 | // long expression in function asymptotic expansion asymptotic_expansion_of_normalized_black_call(). In this last branch, 162 | // here, we therefore take a different tack as follows. 163 | // The "scaled complementary error function" is defined as erfcx(z) = exp(z²)·erfc(z). Cody's implementation of this 164 | // function as published in "Rational Chebyshev approximations for the error function", W. J. Cody, Math. Comp., 1969, pp. 165 | // 631-638, uses rational functions that theoretically approximates erfcx(x) to at least 18 significant decimal digits, 166 | // *without* the use of the exponential function when x>4, which translates to about z<-5.66 in Φ(z). To make use of it, 167 | // we write 168 | // Φ(z) = exp(-z²/2)·erfcx(-z/√2)/2 169 | // 170 | // to transform the normalised black function to 171 | // 172 | // b = ½ · exp(-½(h²+t²)) · [ erfcx(-(h+t)/√2) - erfcx(-(h-t)/√2) ] 173 | // 174 | // which now involves only one exponential, instead of three, when |h|+|t| > 5.66 , and the difference inside the 175 | // square bracket is between the evaluation of two rational functions, which, typically, according to Marsaglia, 176 | // retains the full 16 digits of accuracy (or just a little less than that). 177 | // 178 | const double b = 0.5 * exp(-0.5*(h*h+t*t)) * ( erfcx_cody(-ONE_OVER_SQRT_TWO*(h+t)) - erfcx_cody(-ONE_OVER_SQRT_TWO*(h-t)) ); 179 | return fabs(std::max(b,0.0)); 180 | } 181 | 182 | // Calculation of 183 | // 184 | // b = Φ(h+t)·exp(h·t) - Φ(h-t)·exp(-h·t) 185 | // 186 | // exp(-(h²+t²)/2) 187 | // = --------------- · [ Y(h+t) - Y(h-t) ] 188 | // √(2π) 189 | // with 190 | // Y(z) := Φ(z)/φ(z) 191 | // 192 | // using an expansion of Y(h+t)-Y(h-t) for small t to twelvth order in t. 193 | // Theoretically accurate to (better than) precision ε = 2.23E-16 when h<=0 and t < τ with τ := 2·ε^(1/16) ≈ 0.21. 194 | // The main bottleneck for precision is the coefficient a:=1+h·Y(h) when |h|>1 . 195 | double small_t_expansion_of_normalized_black_call(double h, double t){ 196 | // Y(h) := Φ(h)/φ(h) = √(π/2)·erfcx(-h/√2) 197 | // a := 1+h·Y(h) --- Note that due to h<0, and h·Y(h) -> -1 (from above) as h -> -∞, we also have that a>0 and a -> 0 as h -> -∞ 198 | // w := t² , h2 := h² 199 | const double a = 1+h*(0.5*SQRT_TWO_PI)*erfcx_cody(-ONE_OVER_SQRT_TWO*h), w=t*t, h2=h*h; 200 | const double expansion = 2*t*(a+w*((-1+3*a+a*h2)/6+w*((-7+15*a+h2*(-1+10*a+a*h2))/120+w*((-57+105*a+h2*(-18+105*a+h2*(-1+21*a+a*h2)))/5040+w*((-561+945*a+h2*(-285+1260*a+h2*(-33+378*a+h2*(-1+36*a+a*h2))))/362880+w*((-6555+10395*a+h2*(-4680+17325*a+h2*(-840+6930*a+h2*(-52+990*a+h2*(-1+55*a+a*h2)))))/39916800+((-89055+135135*a+h2*(-82845+270270*a+h2*(-20370+135135*a+h2*(-1926+25740*a+h2*(-75+2145*a+h2*(-1+78*a+a*h2))))))*w)/6227020800.0)))))); 201 | const double b = ONE_OVER_SQRT_TWO_PI*exp((-0.5*(h*h+t*t)))*expansion; 202 | return fabs(std::max(b,0.0)); 203 | } 204 | 205 | namespace { /* τ */ static const double small_t_expansion_of_normalized_black_threshold = 2*SIXTEENTH_ROOT_DBL_EPSILON; } 206 | 207 | // b(x,s) = Φ(x/s+s/2)·exp(x/2) - Φ(x/s-s/2)·exp(-x/2) 208 | // = Φ(h+t)·exp(x/2) - Φ(h-t)·exp(-x/2) 209 | // with 210 | // h = x/s and t = s/2 211 | double normalized_black_call_using_norm_cdf(double x, double s){ 212 | const double h = x/s, t = 0.5*s, b_max = exp(0.5*x), b = norm_cdf(h + t) * b_max - norm_cdf(h - t) / b_max; 213 | return fabs(std::max(b,0.0)); 214 | } 215 | 216 | EXPORT_EXTERN_C double normalised_black_call(double x, double s) { 217 | if (x>0) 218 | return normalised_intrinsic_call(x)+normalised_black_call(-x,s); 219 | const double ax = fabs(x); 220 | if (s<=ax*DENORMALIZATION_CUTOFF) 221 | return normalised_intrinsic_call(x); 222 | // Denote h := x/s and t := s/2. 223 | // We evaluate the condition |h|>|η|, i.e., h<η && t < τ+|h|-|η| avoiding any divisions by s , where η = asymptotic_expansion_accuracy_threshold and τ = small_t_expansion_of_normalized_black_threshold . 224 | if ( x < s*asymptotic_expansion_accuracy_threshold && 0.5*s*s+x < s*(small_t_expansion_of_normalized_black_threshold+asymptotic_expansion_accuracy_threshold) ) 225 | // Region 1. 226 | return asymptotic_expansion_of_normalized_black_call(x/s,0.5*s); 227 | if ( 0.5*s < small_t_expansion_of_normalized_black_threshold ) 228 | // Region 2. 229 | return small_t_expansion_of_normalized_black_call(x/s,0.5*s); 230 | // When b is more than, say, about 85% of b_max=exp(x/2), then b is dominated by the first of the two terms in the Black formula, and we retain more accuracy by not attempting to combine the two terms in any way. 231 | // We evaluate the condition h+t>0.85 avoiding any divisions by s. 232 | if ( x+0.5*s*s > s*0.85 ) 233 | // Region 3. 234 | return normalized_black_call_using_norm_cdf(x,s); 235 | // Region 4. 236 | return normalised_black_call_using_erfcx(x/s,0.5*s); 237 | } 238 | 239 | inline double square(double x){ return x*x; } 240 | 241 | EXPORT_EXTERN_C double normalised_vega(double x, double s) { 242 | const double ax = fabs(x); 243 | return (ax<=0) ? ONE_OVER_SQRT_TWO_PI*exp(-0.125*s*s) : ( (s<=0 || s<=ax*SQRT_DBL_MIN) ? 0 : ONE_OVER_SQRT_TWO_PI*exp(-0.5*(square(x/s)+square(0.5*s))) ); 244 | } 245 | 246 | EXPORT_EXTERN_C double normalised_black(double x, double s, double q /* q=±1 */) { return normalised_black_call(q<0?-x:x,s); /* Reciprocal-strike call-put equivalence */ } 247 | 248 | EXPORT_EXTERN_C double black(double F, double K, double sigma, double T, double q /* q=±1 */) { 249 | const double intrinsic = fabs(std::max((q<0?K-F:F-K),0.0)); 250 | // Map in-the-money to out-of-the-money 251 | if (q*(F-K)>0) 252 | return intrinsic + black(F,K,sigma,T,-q); 253 | return std::max(intrinsic,(sqrt(F)*sqrt(K))*normalised_black(log(F/K),sigma*sqrt(T),q)); 254 | } 255 | 256 | #ifdef COMPUTE_LOWER_MAP_DERIVATIVES_INDIVIDUALLY 257 | double f_lower_map(const double x,const double s){ 258 | if (is_below_horizon(x)) 259 | return 0; 260 | if (is_below_horizon(s)) 261 | return 0; 262 | const double z=SQRT_ONE_OVER_THREE*fabs(x)/s, Phi=norm_cdf(-z); 263 | return TWO_PI_OVER_SQRT_TWENTY_SEVEN*fabs(x)*(Phi*Phi*Phi); 264 | } 265 | double d_f_lower_map_d_beta(const double x,const double s){ 266 | if (is_below_horizon(s)) 267 | return 1; 268 | const double z=SQRT_ONE_OVER_THREE*fabs(x)/s, y = z*z, Phi=norm_cdf(-z); 269 | return TWO_PI*y*(Phi*Phi) * exp(y+0.125*s*s); 270 | } 271 | double d2_f_lower_map_d_beta2(const double x,const double s){ 272 | const double ax=fabs(x), z=SQRT_ONE_OVER_THREE*ax/s, y = z*z, s2=s*s, Phi=norm_cdf(-z), phi=norm_pdf(z); 273 | return PI_OVER_SIX * y/(s2*s) * Phi * ( 8*SQRT_THREE*s*ax + (3*s2*(s2-8)-8*x*x)*Phi/phi ) * exp(2*y+0.25*s2); 274 | } 275 | void compute_f_lower_map_and_first_two_derivatives(const double x,const double s,double &f,double &fp,double &fpp){ 276 | f = f_lower_map(x,s); 277 | fp = d_f_lower_map_d_beta(x,s); 278 | fpp = d2_f_lower_map_d_beta2(x,s); 279 | } 280 | #else 281 | void compute_f_lower_map_and_first_two_derivatives(const double x,const double s,double &f,double &fp,double &fpp){ 282 | const double ax=fabs(x), z=SQRT_ONE_OVER_THREE*ax/s, y = z*z, s2=s*s, Phi=norm_cdf(-z), phi=norm_pdf(z); 283 | fpp = PI_OVER_SIX * y/(s2*s) * Phi * ( 8*SQRT_THREE*s*ax + (3*s2*(s2-8)-8*x*x)*Phi/phi ) * exp(2*y+0.25*s2); 284 | if (is_below_horizon(s)) { 285 | fp = 1; 286 | f = 0; 287 | } else { 288 | const double Phi2=Phi*Phi; 289 | fp = TWO_PI*y*Phi2*exp(y+0.125*s*s); 290 | if (is_below_horizon(x)) 291 | f = 0; 292 | else 293 | f = TWO_PI_OVER_SQRT_TWENTY_SEVEN*ax*(Phi2*Phi); 294 | } 295 | } 296 | #endif 297 | 298 | double inverse_f_lower_map(const double x,const double f){ 299 | return is_below_horizon(f) ? 0 : fabs(x/(SQRT_THREE*inverse_norm_cdf( std::pow( f/(TWO_PI_OVER_SQRT_TWENTY_SEVEN*fabs(x)) , 1./3.) ))); 300 | } 301 | 302 | #ifdef COMPUTE_UPPER_MAP_DERIVATIVES_INDIVIDUALLY 303 | double f_upper_map(const double s){ 304 | return norm_cdf(-0.5*s); 305 | } 306 | double d_f_upper_map_d_beta(const double x,const double s){ 307 | return is_below_horizon(x) ? -0.5 : -0.5*exp(0.5*square(x/s)); 308 | } 309 | double d2_f_upper_map_d_beta2(const double x,const double s){ 310 | if (is_below_horizon(x)) 311 | return 0; 312 | const double w = square(x/s); 313 | return SQRT_PI_OVER_TWO*exp(w+0.125*s*s)*w/s; 314 | } 315 | void compute_f_upper_map_and_first_two_derivatives(const double x,const double s,double &f,double &fp,double &fpp){ 316 | f = f_upper_map(s); 317 | fp = d_f_upper_map_d_beta(x,s); 318 | fpp = d2_f_upper_map_d_beta2(x,s); 319 | } 320 | #else 321 | void compute_f_upper_map_and_first_two_derivatives(const double x,const double s,double &f,double &fp,double &fpp){ 322 | f = norm_cdf(-0.5*s); 323 | if (is_below_horizon(x)) { 324 | fp = -0.5; 325 | fpp = 0; 326 | } else { 327 | const double w = square(x/s); 328 | fp = -0.5*exp(0.5*w); 329 | fpp = SQRT_PI_OVER_TWO*exp(w+0.125*s*s)*w/s; 330 | } 331 | } 332 | #endif 333 | 334 | double inverse_f_upper_map(double f){ 335 | return -2.*inverse_norm_cdf(f); 336 | } 337 | 338 | // See http://en.wikipedia.org/wiki/Householder%27s_method for a detailed explanation of the third order Householder iteration. 339 | // 340 | // Given the objective function g(s) whose root x such that 0 = g(s) we seek, iterate 341 | // 342 | // s_n+1 = s_n - (g/g') · [ 1 - (g''/g')·(g/g') ] / [ 1 - (g/g')·( (g''/g') - (g'''/g')·(g/g')/6 ) ] 343 | // 344 | // Denoting newton:=-(g/g'), halley:=(g''/g'), and hh3:=(g'''/g'), this reads 345 | // 346 | // s_n+1 = s_n + newton · [ 1 + halley·newton/2 ] / [ 1 + newton·( halley + hh3·newton/6 ) ] 347 | // 348 | // 349 | // NOTE that this function returns 0 when beta0) { 354 | beta = fabs(std::max(beta-normalised_intrinsic(x, q),0.)); 355 | q = -q; 356 | } 357 | // Map puts to calls 358 | if (q<0){ 359 | x = -x; 360 | q = -q; 361 | } 362 | if (beta<=0) // For negative or zero prices we return 0. 363 | return implied_volatility_output(0,0); 364 | if (beta=b_max) 368 | return implied_volatility_output(0,VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM); 369 | int iterations=0, direction_reversal_count = 0; 370 | double f=-DBL_MAX, s=-DBL_MAX, ds=s, ds_previous=0, s_left=DBL_MIN, s_right=DBL_MAX; 371 | // The temptation is great to use the optimised form b_c = exp(x/2)/2-exp(-x/2)·Phi(sqrt(-2·x)) but that would require implementing all of the above types of round-off and over/underflow handling for this expression, too. 372 | const double s_c=sqrt(fabs(2*x)), b_c = normalised_black_call(x,s_c), v_c = normalised_vega(x, s_c); 373 | // Four branches. 374 | if ( beta0)) { // This can happen due to roundoff truncation for extreme values such as |x|>500. 382 | // We switch to quadratic interpolation using f(0)≡0, f(b_l), and f'(0)≡1 to specify the quadratic. 383 | const double t = beta/b_l; 384 | f = (f_lower_map_l*t + b_l*(1-t)) * t; 385 | } 386 | s = inverse_f_lower_map(x,f); 387 | s_right = s_l; 388 | // 389 | // In this branch, which comprises the lowest segment, the objective function is 390 | // g(s) = 1/ln(b(x,s)) - 1/ln(beta) 391 | // ≡ 1/ln(b(s)) - 1/ln(beta) 392 | // This makes 393 | // g' = -b'/(b·ln(b)²) 394 | // newton = -g/g' = (ln(beta)-ln(b))·ln(b)/ln(beta)·b/b' 395 | // halley = g''/g' = b''/b' - b'/b·(1+2/ln(b)) 396 | // hh3 = g'''/g' = b'''/b' + 2(b'/b)²·(1+3/ln(b)·(1+1/ln(b))) - 3(b''/b)·(1+2/ln(b)) 397 | // 398 | // The Householder(3) iteration is 399 | // s_n+1 = s_n + newton · [ 1 + halley·newton/2 ] / [ 1 + newton·( halley + hh3·newton/6 ) ] 400 | // 401 | for (; iterationsDBL_EPSILON*s; ++iterations){ 402 | if (ds*ds_previous<0) 403 | ++direction_reversal_count; 404 | if ( iterations>0 && ( 3==direction_reversal_count || !(s>s_left && s 500. 407 | s = 0.5*(s_left+s_right); 408 | if (s_right-s_left<=DBL_EPSILON*s) break; 409 | direction_reversal_count = 0; 410 | ds = 0; 411 | } 412 | ds_previous=ds; 413 | const double b = normalised_black_call(x,s), bp = normalised_vega(x, s); 414 | if ( b>beta && ss_left ) s_left=s; // Tighten the bracket if applicable. 415 | if (b<=0||bp<=0) // Numerical underflow. Switch to binary nesting for this iteration. 416 | ds = 0.5*(s_left+s_right)-s; 417 | else { 418 | const double ln_b=log(b), ln_beta=log(beta), bpob=bp/b, h=x/s, b_halley = h*h/s-s/4, newton = (ln_beta-ln_b)*ln_b/ln_beta/bpob, halley = b_halley-bpob*(1+2/ln_b); 419 | const double b_hh3 = b_halley*b_halley-3*square(h/s)-0.25, hh3 = b_hh3+2*square(bpob)*(1+3/ln_b*(1+1/ln_b))-3*b_halley*bpob*(1+2/ln_b); 420 | ds = newton * householder_factor(newton,halley,hh3); 421 | } 422 | s += ds = std::max(-0.5*s , ds ); 423 | } 424 | return implied_volatility_output(iterations,s); 425 | } else { 426 | const double v_l = normalised_vega(x, s_l), r_lm = convex_rational_cubic_control_parameter_to_fit_second_derivative_at_right_side(b_l,b_c,s_l,s_c,1/v_l,1/v_c,0.0,false); 427 | s = rational_cubic_interpolation(beta,b_l,b_c,s_l,s_c,1/v_l,1/v_c,r_lm); 428 | s_left = s_l; 429 | s_right = s_c; 430 | } 431 | } else { 432 | const double s_h = v_c>DBL_MIN ? s_c+(b_max-b_c)/v_c : s_c, b_h = normalised_black_call(x,s_h); 433 | if(beta<=b_h){ 434 | const double v_h = normalised_vega(x, s_h), r_hm = convex_rational_cubic_control_parameter_to_fit_second_derivative_at_left_side(b_c,b_h,s_c,s_h,1/v_c,1/v_h,0.0,false); 435 | s = rational_cubic_interpolation(beta,b_c,b_h,s_c,s_h,1/v_c,1/v_h,r_hm); 436 | s_left = s_c; 437 | s_right = s_h; 438 | } else { 439 | double f_upper_map_h, d_f_upper_map_h_d_beta, d2_f_upper_map_h_d_beta2; 440 | compute_f_upper_map_and_first_two_derivatives(x,s_h,f_upper_map_h,d_f_upper_map_h_d_beta,d2_f_upper_map_h_d_beta2); 441 | if ( d2_f_upper_map_h_d_beta2>-SQRT_DBL_MAX && d2_f_upper_map_h_d_beta20.5*b_max) { // Else we better drop through and let the objective function be g(s) = b(x,s)-beta. 452 | // 453 | // In this branch, which comprises the upper segment, the objective function is 454 | // g(s) = ln(b_max-beta)-ln(b_max-b(x,s)) 455 | // ≡ ln((b_max-beta)/(b_max-b(s))) 456 | // This makes 457 | // g' = b'/(b_max-b) 458 | // newton = -g/g' = ln((b_max-b)/(b_max-beta))·(b_max-b)/b' 459 | // halley = g''/g' = b''/b' + b'/(b_max-b) 460 | // hh3 = g'''/g' = b'''/b' + g'·(2g'+3b''/b') 461 | // and the iteration is 462 | // s_n+1 = s_n + newton · [ 1 + halley·newton/2 ] / [ 1 + newton·( halley + hh3·newton/6 ) ]. 463 | // 464 | for (; iterationsDBL_EPSILON*s; ++iterations){ 465 | if (ds*ds_previous<0) 466 | ++direction_reversal_count; 467 | if ( iterations>0 && ( 3==direction_reversal_count || !(s>s_left && s 500. 470 | s = 0.5*(s_left+s_right); 471 | if (s_right-s_left<=DBL_EPSILON*s) break; 472 | direction_reversal_count = 0; 473 | ds = 0; 474 | } 475 | ds_previous=ds; 476 | const double b = normalised_black_call(x,s), bp = normalised_vega(x, s); 477 | if ( b>beta && ss_left ) s_left=s; // Tighten the bracket if applicable. 478 | if (b>=b_max||bp<=DBL_MIN) // Numerical underflow. Switch to binary nesting for this iteration. 479 | ds = 0.5*(s_left+s_right)-s; 480 | else { 481 | const double b_max_minus_b = b_max-b, g = log((b_max-beta)/b_max_minus_b), gp = bp/b_max_minus_b; 482 | const double b_halley = square(x/s)/s-s/4, b_hh3 = b_halley*b_halley-3*square(x/(s*s))-0.25; 483 | const double newton = -g/gp, halley = b_halley+gp, hh3 = b_hh3+gp*(2*gp+3*b_halley); 484 | ds = newton * householder_factor(newton,halley,hh3); 485 | } 486 | s += ds = std::max(-0.5*s , ds ); 487 | } 488 | return implied_volatility_output(iterations,s); 489 | } 490 | } 491 | } 492 | // In this branch, which comprises the two middle segments, the objective function is g(s) = b(x,s)-beta, or g(s) = b(s) - beta, for short. 493 | // This makes 494 | // newton = -g/g' = -(b-beta)/b' 495 | // halley = g''/g' = b''/b' = x²/s³-s/4 496 | // hh3 = g'''/g' = b'''/b' = halley² - 3·(x/s²)² - 1/4 497 | // and the iteration is 498 | // s_n+1 = s_n + newton · [ 1 + halley·newton/2 ] / [ 1 + newton·( halley + hh3·newton/6 ) ]. 499 | // 500 | for (; iterationsDBL_EPSILON*s; ++iterations){ 501 | if (ds*ds_previous<0) 502 | ++direction_reversal_count; 503 | if ( iterations>0 && ( 3==direction_reversal_count || !(s>s_left && s 500. 506 | s = 0.5*(s_left+s_right); 507 | if (s_right-s_left<=DBL_EPSILON*s) break; 508 | direction_reversal_count = 0; 509 | ds = 0; 510 | } 511 | ds_previous=ds; 512 | const double b = normalised_black_call(x,s), bp = normalised_vega(x, s); 513 | if ( b>beta && ss_left ) s_left=s; // Tighten the bracket if applicable. 514 | const double newton = (beta-b)/bp, halley = square(x/s)/s-s/4, hh3 = halley*halley-3*square(x/(s*s))-0.25; 515 | s += ds = std::max(-0.5*s , newton * householder_factor(newton,halley,hh3) ); 516 | } 517 | return implied_volatility_output(iterations,s); 518 | } 519 | 520 | EXPORT_EXTERN_C double implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(double price, double F, double K, double T, double q /* q=±1 */, int N){ 521 | const double intrinsic = fabs(std::max((q<0?K-F:F-K),0.0)); 522 | if (price=max_price) 526 | return implied_volatility_output(0,VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM); 527 | const double x = log(F/K); 528 | // Map in-the-money to out-of-the-money 529 | if (q*x>0) { 530 | price = fabs(std::max(price-intrinsic,0.0)); 531 | q = -q; 532 | } 533 | return unchecked_normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(price/(sqrt(F)*sqrt(K)), x, q, N)/sqrt(T); 534 | } 535 | 536 | EXPORT_EXTERN_C double implied_volatility_from_a_transformed_rational_guess(double price, double F, double K, double T, double q /* q=±1 */){ 537 | return implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(price,F,K,T,q,implied_volatility_maximum_iterations.data); 538 | } 539 | 540 | EXPORT_EXTERN_C double normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(double beta, double x, double q /* q=±1 */, int N){ 541 | // Map in-the-money to out-of-the-money 542 | if (q*x>0) { 543 | beta -= normalised_intrinsic(x, q); 544 | q = -q; 545 | } 546 | if (beta<0) 547 | return implied_volatility_output(0,VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_BELOW_INTRINSIC); 548 | return unchecked_normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta, x, q, N); 549 | } 550 | 551 | EXPORT_EXTERN_C double normalised_implied_volatility_from_a_transformed_rational_guess(double beta, double x, double q /* q=±1 */){ 552 | return normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta,x,q,implied_volatility_maximum_iterations.data); 553 | } 554 | -------------------------------------------------------------------------------- /src/LetsBeRational.i: -------------------------------------------------------------------------------- 1 | /* lbr.i */ 2 | %module LetsBeRational 3 | 4 | 5 | 6 | 7 | %{ 8 | #ifdef __cplusplus 9 | # define EXTERN_C extern "C" 10 | #else 11 | # define EXTERN_C extern 12 | #endif 13 | EXTERN_C double norm_cdf(double z); 14 | EXTERN_C double normalised_black_call(double x, double s); 15 | EXTERN_C double normalised_vega(double x, double s); 16 | EXTERN_C double normalised_black(double x, double s, double q /* q=±1 */); 17 | EXTERN_C double black(double F, double K, double sigma, double T, double q /* q=±1 */); 18 | EXTERN_C double implied_volatility_from_a_transformed_rational_guess(double price, double F, double K, double T, double q /* q=±1 */); 19 | EXTERN_C double implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(double price, double F, double K, double T, double q /* q=±1 */, int N); 20 | EXTERN_C double normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(double beta, double x, double q /* q=±1 */, int N); 21 | EXTERN_C double normalised_implied_volatility_from_a_transformed_rational_guess(double beta, double x, double q /* q=±1 */); 22 | 23 | %} 24 | 25 | extern double norm_cdf(double z); 26 | extern double normalised_black_call(double x, double s); 27 | extern double normalised_vega(double x, double s); 28 | extern double normalised_black(double x, double s, double q /* q=±1 */); 29 | extern double black(double F, double K, double sigma, double T, double q /* q=±1 */); 30 | extern double implied_volatility_from_a_transformed_rational_guess(double price, double F, double K, double T, double q /* q=±1 */); 31 | extern double implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(double price, double F, double K, double T, double q /* q=±1 */, int N); 32 | extern double normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(double beta, double x, double q /* q=±1 */, int N); 33 | extern double normalised_implied_volatility_from_a_transformed_rational_guess(double beta, double x, double q /* q=±1 */); 34 | 35 | -------------------------------------------------------------------------------- /src/LetsBeRational.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (http://www.swig.org). 2 | # Version 3.0.5 3 | # 4 | # Do not make changes to this file unless you know what you are doing--modify 5 | # the SWIG interface file instead. 6 | 7 | 8 | 9 | 10 | 11 | from sys import version_info 12 | if version_info >= (2, 6, 0): 13 | def swig_import_helper(): 14 | from os.path import dirname 15 | import imp 16 | fp = None 17 | try: 18 | fp, pathname, description = imp.find_module('_LetsBeRational', [dirname(__file__)]) 19 | except ImportError: 20 | import _LetsBeRational 21 | return _LetsBeRational 22 | if fp is not None: 23 | try: 24 | _mod = imp.load_module('_LetsBeRational', fp, pathname, description) 25 | finally: 26 | fp.close() 27 | return _mod 28 | _LetsBeRational = swig_import_helper() 29 | del swig_import_helper 30 | else: 31 | import _LetsBeRational 32 | del version_info 33 | try: 34 | _swig_property = property 35 | except NameError: 36 | pass # Python < 2.2 doesn't have 'property'. 37 | 38 | 39 | def _swig_setattr_nondynamic(self, class_type, name, value, static=1): 40 | if (name == "thisown"): 41 | return self.this.own(value) 42 | if (name == "this"): 43 | if type(value).__name__ == 'SwigPyObject': 44 | self.__dict__[name] = value 45 | return 46 | method = class_type.__swig_setmethods__.get(name, None) 47 | if method: 48 | return method(self, value) 49 | if (not static): 50 | if _newclass: 51 | object.__setattr__(self, name, value) 52 | else: 53 | self.__dict__[name] = value 54 | else: 55 | raise AttributeError("You cannot add attributes to %s" % self) 56 | 57 | 58 | def _swig_setattr(self, class_type, name, value): 59 | return _swig_setattr_nondynamic(self, class_type, name, value, 0) 60 | 61 | 62 | def _swig_getattr_nondynamic(self, class_type, name, static=1): 63 | if (name == "thisown"): 64 | return self.this.own() 65 | method = class_type.__swig_getmethods__.get(name, None) 66 | if method: 67 | return method(self) 68 | if (not static): 69 | return object.__getattr__(self, name) 70 | else: 71 | raise AttributeError(name) 72 | 73 | def _swig_getattr(self, class_type, name): 74 | return _swig_getattr_nondynamic(self, class_type, name, 0) 75 | 76 | 77 | def _swig_repr(self): 78 | try: 79 | strthis = "proxy of " + self.this.__repr__() 80 | except: 81 | strthis = "" 82 | return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) 83 | 84 | try: 85 | _object = object 86 | _newclass = 1 87 | except AttributeError: 88 | class _object: 89 | pass 90 | _newclass = 0 91 | 92 | 93 | 94 | def norm_cdf(z): 95 | return _LetsBeRational.norm_cdf(z) 96 | norm_cdf = _LetsBeRational.norm_cdf 97 | 98 | def normalised_black_call(x, s): 99 | return _LetsBeRational.normalised_black_call(x, s) 100 | normalised_black_call = _LetsBeRational.normalised_black_call 101 | 102 | def normalised_vega(x, s): 103 | return _LetsBeRational.normalised_vega(x, s) 104 | normalised_vega = _LetsBeRational.normalised_vega 105 | 106 | def normalised_black(x, s, q): 107 | return _LetsBeRational.normalised_black(x, s, q) 108 | normalised_black = _LetsBeRational.normalised_black 109 | 110 | def black(F, K, sigma, T, q): 111 | return _LetsBeRational.black(F, K, sigma, T, q) 112 | black = _LetsBeRational.black 113 | 114 | def implied_volatility_from_a_transformed_rational_guess(price, F, K, T, q): 115 | return _LetsBeRational.implied_volatility_from_a_transformed_rational_guess(price, F, K, T, q) 116 | implied_volatility_from_a_transformed_rational_guess = _LetsBeRational.implied_volatility_from_a_transformed_rational_guess 117 | 118 | def implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(price, F, K, T, q, N): 119 | return _LetsBeRational.implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(price, F, K, T, q, N) 120 | implied_volatility_from_a_transformed_rational_guess_with_limited_iterations = _LetsBeRational.implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 121 | 122 | def normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta, x, q, N): 123 | return _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta, x, q, N) 124 | normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations = _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations 125 | 126 | def normalised_implied_volatility_from_a_transformed_rational_guess(beta, x, q): 127 | return _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess(beta, x, q) 128 | normalised_implied_volatility_from_a_transformed_rational_guess = _LetsBeRational.normalised_implied_volatility_from_a_transformed_rational_guess 129 | # This file is compatible with both classic and new-style classes. 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/erf_cody.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Original Fortran code taken from http://www.netlib.org/specfun/erf, compiled with f2c, and adapted by hand. 3 | // 4 | // Created with command line f2c -C++ -c -a -krd -r8 cody_erf.f 5 | // 6 | // Translated by f2c (version 20100827). 7 | // 8 | 9 | // 10 | // This source code resides at www.jaeckel.org/LetsBeRational.7z . 11 | // 12 | // ====================================================================================== 13 | // WARRANTY DISCLAIMER 14 | // The Software is provided "as is" without warranty of any kind, either express or implied, 15 | // including without limitation any implied warranties of condition, uninterrupted use, 16 | // merchantability, fitness for a particular purpose, or non-infringement. 17 | // ====================================================================================== 18 | // 19 | 20 | #if defined( _DEBUG ) || defined( BOUNDS_CHECK_STL_ARRAYS ) 21 | #define _SECURE_SCL 1 22 | #define _SECURE_SCL_THROWS 1 23 | #define _SCL_SECURE_NO_WARNINGS 24 | #define _HAS_ITERATOR_DEBUGGING 0 25 | #else 26 | #define _SECURE_SCL 0 27 | #endif 28 | #if defined(_MSC_VER) 29 | # define NOMINMAX // to suppress MSVC's definitions of min() and max() 30 | // These four pragmas are the equivalent to /fp:fast. 31 | # pragma float_control( except, off ) 32 | # pragma float_control( precise, off ) 33 | # pragma fp_contract( on ) 34 | # pragma fenv_access( off ) 35 | #endif 36 | 37 | #include "normaldistribution.h" 38 | #include 39 | #include 40 | 41 | namespace { 42 | inline double d_int(const double x){ return( (x>0) ? floor(x) : -floor(-x) ); } 43 | } 44 | 45 | /*< SUBROUTINE CALERF(ARG,RESULT,JINT) >*/ 46 | double calerf(double x, const int jint) { 47 | 48 | static const double a[5] = { 3.1611237438705656,113.864154151050156,377.485237685302021,3209.37758913846947,.185777706184603153 }; 49 | static const double b[4] = { 23.6012909523441209,244.024637934444173,1282.61652607737228,2844.23683343917062 }; 50 | static const double c__[9] = { .564188496988670089,8.88314979438837594,66.1191906371416295,298.635138197400131,881.95222124176909,1712.04761263407058,2051.07837782607147,1230.33935479799725,2.15311535474403846e-8 }; 51 | static const double d__[8] = { 15.7449261107098347,117.693950891312499,537.181101862009858,1621.38957456669019,3290.79923573345963,4362.61909014324716,3439.36767414372164,1230.33935480374942 }; 52 | static const double p[6] = { .305326634961232344,.360344899949804439,.125781726111229246,.0160837851487422766,6.58749161529837803e-4,.0163153871373020978 }; 53 | static const double q[5] = { 2.56852019228982242,1.87295284992346047,.527905102951428412,.0605183413124413191,.00233520497626869185 }; 54 | 55 | static const double zero = 0.; 56 | static const double half = .5; 57 | static const double one = 1.; 58 | static const double two = 2.; 59 | static const double four = 4.; 60 | static const double sqrpi = 0.56418958354775628695; 61 | static const double thresh = .46875; 62 | static const double sixten = 16.; 63 | 64 | double y, del, ysq, xden, xnum, result; 65 | 66 | /* ------------------------------------------------------------------ */ 67 | /* This packet evaluates erf(x), erfc(x), and exp(x*x)*erfc(x) */ 68 | /* for a real argument x. It contains three FUNCTION type */ 69 | /* subprograms: ERF, ERFC, and ERFCX (or DERF, DERFC, and DERFCX), */ 70 | /* and one SUBROUTINE type subprogram, CALERF. The calling */ 71 | /* statements for the primary entries are: */ 72 | /* Y=ERF(X) (or Y=DERF(X)), */ 73 | /* Y=ERFC(X) (or Y=DERFC(X)), */ 74 | /* and */ 75 | /* Y=ERFCX(X) (or Y=DERFCX(X)). */ 76 | /* The routine CALERF is intended for internal packet use only, */ 77 | /* all computations within the packet being concentrated in this */ 78 | /* routine. The function subprograms invoke CALERF with the */ 79 | /* statement */ 80 | /* CALL CALERF(ARG,RESULT,JINT) */ 81 | /* where the parameter usage is as follows */ 82 | /* Function Parameters for CALERF */ 83 | /* call ARG Result JINT */ 84 | /* ERF(ARG) ANY REAL ARGUMENT ERF(ARG) 0 */ 85 | /* ERFC(ARG) ABS(ARG) .LT. XBIG ERFC(ARG) 1 */ 86 | /* ERFCX(ARG) XNEG .LT. ARG .LT. XMAX ERFCX(ARG) 2 */ 87 | /* The main computation evaluates near-minimax approximations */ 88 | /* from "Rational Chebyshev approximations for the error function" */ 89 | /* by W. J. Cody, Math. Comp., 1969, PP. 631-638. This */ 90 | /* transportable program uses rational functions that theoretically */ 91 | /* approximate erf(x) and erfc(x) to at least 18 significant */ 92 | /* decimal digits. The accuracy achieved depends on the arithmetic */ 93 | /* system, the compiler, the intrinsic functions, and proper */ 94 | /* selection of the machine-dependent constants. */ 95 | /* ******************************************************************* */ 96 | /* ******************************************************************* */ 97 | /* Explanation of machine-dependent constants */ 98 | /* XMIN = the smallest positive floating-point number. */ 99 | /* XINF = the largest positive finite floating-point number. */ 100 | /* XNEG = the largest negative argument acceptable to ERFCX; */ 101 | /* the negative of the solution to the equation */ 102 | /* 2*exp(x*x) = XINF. */ 103 | /* XSMALL = argument below which erf(x) may be represented by */ 104 | /* 2*x/sqrt(pi) and above which x*x will not underflow. */ 105 | /* A conservative value is the largest machine number X */ 106 | /* such that 1.0 + X = 1.0 to machine precision. */ 107 | /* XBIG = largest argument acceptable to ERFC; solution to */ 108 | /* the equation: W(x) * (1-0.5/x**2) = XMIN, where */ 109 | /* W(x) = exp(-x*x)/[x*sqrt(pi)]. */ 110 | /* XHUGE = argument above which 1.0 - 1/(2*x*x) = 1.0 to */ 111 | /* machine precision. A conservative value is */ 112 | /* 1/[2*sqrt(XSMALL)] */ 113 | /* XMAX = largest acceptable argument to ERFCX; the minimum */ 114 | /* of XINF and 1/[sqrt(pi)*XMIN]. */ 115 | // The numbers below were preselected for IEEE . 116 | static const double xinf = 1.79e308; 117 | static const double xneg = -26.628; 118 | static const double xsmall = 1.11e-16; 119 | static const double xbig = 26.543; 120 | static const double xhuge = 6.71e7; 121 | static const double xmax = 2.53e307; 122 | /* Approximate values for some important machines are: */ 123 | /* XMIN XINF XNEG XSMALL */ 124 | /* CDC 7600 (S.P.) 3.13E-294 1.26E+322 -27.220 7.11E-15 */ 125 | /* CRAY-1 (S.P.) 4.58E-2467 5.45E+2465 -75.345 7.11E-15 */ 126 | /* IEEE (IBM/XT, */ 127 | /* SUN, etc.) (S.P.) 1.18E-38 3.40E+38 -9.382 5.96E-8 */ 128 | /* IEEE (IBM/XT, */ 129 | /* SUN, etc.) (D.P.) 2.23D-308 1.79D+308 -26.628 1.11D-16 */ 130 | /* IBM 195 (D.P.) 5.40D-79 7.23E+75 -13.190 1.39D-17 */ 131 | /* UNIVAC 1108 (D.P.) 2.78D-309 8.98D+307 -26.615 1.73D-18 */ 132 | /* VAX D-Format (D.P.) 2.94D-39 1.70D+38 -9.345 1.39D-17 */ 133 | /* VAX G-Format (D.P.) 5.56D-309 8.98D+307 -26.615 1.11D-16 */ 134 | /* XBIG XHUGE XMAX */ 135 | /* CDC 7600 (S.P.) 25.922 8.39E+6 1.80X+293 */ 136 | /* CRAY-1 (S.P.) 75.326 8.39E+6 5.45E+2465 */ 137 | /* IEEE (IBM/XT, */ 138 | /* SUN, etc.) (S.P.) 9.194 2.90E+3 4.79E+37 */ 139 | /* IEEE (IBM/XT, */ 140 | /* SUN, etc.) (D.P.) 26.543 6.71D+7 2.53D+307 */ 141 | /* IBM 195 (D.P.) 13.306 1.90D+8 7.23E+75 */ 142 | /* UNIVAC 1108 (D.P.) 26.582 5.37D+8 8.98D+307 */ 143 | /* VAX D-Format (D.P.) 9.269 1.90D+8 1.70D+38 */ 144 | /* VAX G-Format (D.P.) 26.569 6.71D+7 8.98D+307 */ 145 | /* ******************************************************************* */ 146 | /* ******************************************************************* */ 147 | /* Error returns */ 148 | /* The program returns ERFC = 0 for ARG .GE. XBIG; */ 149 | /* ERFCX = XINF for ARG .LT. XNEG; */ 150 | /* and */ 151 | /* ERFCX = 0 for ARG .GE. XMAX. */ 152 | /* Intrinsic functions required are: */ 153 | /* ABS, AINT, EXP */ 154 | /* Author: W. J. Cody */ 155 | /* Mathematics and Computer Science Division */ 156 | /* Argonne National Laboratory */ 157 | /* Argonne, IL 60439 */ 158 | /* Latest modification: March 19, 1990 */ 159 | /* ------------------------------------------------------------------ */ 160 | /*< INTEGER I,JINT >*/ 161 | /* S REAL */ 162 | /*< >*/ 163 | /*< DIMENSION A(5),B(4),C(9),D(8),P(6),Q(5) >*/ 164 | /* ------------------------------------------------------------------ */ 165 | /* Mathematical constants */ 166 | /* ------------------------------------------------------------------ */ 167 | /* S DATA FOUR,ONE,HALF,TWO,ZERO/4.0E0,1.0E0,0.5E0,2.0E0,0.0E0/, */ 168 | /* S 1 SQRPI/5.6418958354775628695E-1/,THRESH/0.46875E0/, */ 169 | /* S 2 SIXTEN/16.0E0/ */ 170 | /*< >*/ 171 | /* ------------------------------------------------------------------ */ 172 | /* Machine-dependent constants */ 173 | /* ------------------------------------------------------------------ */ 174 | /* S DATA XINF,XNEG,XSMALL/3.40E+38,-9.382E0,5.96E-8/, */ 175 | /* S 1 XBIG,XHUGE,XMAX/9.194E0,2.90E3,4.79E37/ */ 176 | /*< >*/ 177 | /* ------------------------------------------------------------------ */ 178 | /* Coefficients for approximation to erf in first interval */ 179 | /* ------------------------------------------------------------------ */ 180 | /* S DATA A/3.16112374387056560E00,1.13864154151050156E02, */ 181 | /* S 1 3.77485237685302021E02,3.20937758913846947E03, */ 182 | /* S 2 1.85777706184603153E-1/ */ 183 | /* S DATA B/2.36012909523441209E01,2.44024637934444173E02, */ 184 | /* S 1 1.28261652607737228E03,2.84423683343917062E03/ */ 185 | /*< >*/ 186 | /*< >*/ 187 | /* ------------------------------------------------------------------ */ 188 | /* Coefficients for approximation to erfc in second interval */ 189 | /* ------------------------------------------------------------------ */ 190 | /* S DATA C/5.64188496988670089E-1,8.88314979438837594E0, */ 191 | /* S 1 6.61191906371416295E01,2.98635138197400131E02, */ 192 | /* S 2 8.81952221241769090E02,1.71204761263407058E03, */ 193 | /* S 3 2.05107837782607147E03,1.23033935479799725E03, */ 194 | /* S 4 2.15311535474403846E-8/ */ 195 | /* S DATA D/1.57449261107098347E01,1.17693950891312499E02, */ 196 | /* S 1 5.37181101862009858E02,1.62138957456669019E03, */ 197 | /* S 2 3.29079923573345963E03,4.36261909014324716E03, */ 198 | /* S 3 3.43936767414372164E03,1.23033935480374942E03/ */ 199 | /*< >*/ 200 | /*< >*/ 201 | /* ------------------------------------------------------------------ */ 202 | /* Coefficients for approximation to erfc in third interval */ 203 | /* ------------------------------------------------------------------ */ 204 | /* S DATA P/3.05326634961232344E-1,3.60344899949804439E-1, */ 205 | /* S 1 1.25781726111229246E-1,1.60837851487422766E-2, */ 206 | /* S 2 6.58749161529837803E-4,1.63153871373020978E-2/ */ 207 | /* S DATA Q/2.56852019228982242E00,1.87295284992346047E00, */ 208 | /* S 1 5.27905102951428412E-1,6.05183413124413191E-2, */ 209 | /* S 2 2.33520497626869185E-3/ */ 210 | /*< >*/ 211 | /*< >*/ 212 | /* ------------------------------------------------------------------ */ 213 | /*< X = ARG >*/ 214 | // x = *arg; 215 | /*< Y = ABS(X) >*/ 216 | y = fabs(x); 217 | /*< IF (Y .LE. THRESH) THEN >*/ 218 | if (y <= thresh) { 219 | /* ------------------------------------------------------------------ */ 220 | /* Evaluate erf for |X| <= 0.46875 */ 221 | /* ------------------------------------------------------------------ */ 222 | /*< YSQ = ZERO >*/ 223 | ysq = zero; 224 | /*< IF (Y .GT. XSMALL) YSQ = Y * Y >*/ 225 | if (y > xsmall) { 226 | ysq = y * y; 227 | } 228 | /*< XNUM = A(5)*YSQ >*/ 229 | xnum = a[4] * ysq; 230 | /*< XDEN = YSQ >*/ 231 | xden = ysq; 232 | /*< DO 20 I = 1, 3 >*/ 233 | for (int i__ = 1; i__ <= 3; ++i__) { 234 | /*< XNUM = (XNUM + A(I)) * YSQ >*/ 235 | xnum = (xnum + a[i__ - 1]) * ysq; 236 | /*< XDEN = (XDEN + B(I)) * YSQ >*/ 237 | xden = (xden + b[i__ - 1]) * ysq; 238 | /*< 20 CONTINUE >*/ 239 | /* L20: */ 240 | } 241 | /*< RESULT = X * (XNUM + A(4)) / (XDEN + B(4)) >*/ 242 | result = x * (xnum + a[3]) / (xden + b[3]); 243 | /*< IF (JINT .NE. 0) RESULT = ONE - RESULT >*/ 244 | if (jint != 0) { 245 | result = one - result; 246 | } 247 | /*< IF (JINT .EQ. 2) RESULT = EXP(YSQ) * RESULT >*/ 248 | if (jint == 2) { 249 | result = exp(ysq) * result; 250 | } 251 | /*< GO TO 800 >*/ 252 | goto L800; 253 | /* ------------------------------------------------------------------ */ 254 | /* Evaluate erfc for 0.46875 <= |X| <= 4.0 */ 255 | /* ------------------------------------------------------------------ */ 256 | /*< ELSE IF (Y .LE. FOUR) THEN >*/ 257 | } else if (y <= four) { 258 | /*< XNUM = C(9)*Y >*/ 259 | xnum = c__[8] * y; 260 | /*< XDEN = Y >*/ 261 | xden = y; 262 | /*< DO 120 I = 1, 7 >*/ 263 | for (int i__ = 1; i__ <= 7; ++i__) { 264 | /*< XNUM = (XNUM + C(I)) * Y >*/ 265 | xnum = (xnum + c__[i__ - 1]) * y; 266 | /*< XDEN = (XDEN + D(I)) * Y >*/ 267 | xden = (xden + d__[i__ - 1]) * y; 268 | /*< 120 CONTINUE >*/ 269 | /* L120: */ 270 | } 271 | /*< RESULT = (XNUM + C(8)) / (XDEN + D(8)) >*/ 272 | result = (xnum + c__[7]) / (xden + d__[7]); 273 | /*< IF (JINT .NE. 2) THEN >*/ 274 | if (jint != 2) { 275 | /*< YSQ = AINT(Y*SIXTEN)/SIXTEN >*/ 276 | double d__1 = y * sixten; 277 | ysq = d_int(d__1) / sixten; 278 | /*< DEL = (Y-YSQ)*(Y+YSQ) >*/ 279 | del = (y - ysq) * (y + ysq); 280 | /*< RESULT = EXP(-YSQ*YSQ) * EXP(-DEL) * RESULT >*/ 281 | d__1 = exp(-ysq * ysq) * exp(-del); 282 | result = d__1 * result; 283 | /*< END IF >*/ 284 | } 285 | /* ------------------------------------------------------------------ */ 286 | /* Evaluate erfc for |X| > 4.0 */ 287 | /* ------------------------------------------------------------------ */ 288 | /*< ELSE >*/ 289 | } else { 290 | /*< RESULT = ZERO >*/ 291 | result = zero; 292 | /*< IF (Y .GE. XBIG) THEN >*/ 293 | if (y >= xbig) { 294 | /*< IF ((JINT .NE. 2) .OR. (Y .GE. XMAX)) GO TO 300 >*/ 295 | if (jint != 2 || y >= xmax) { 296 | goto L300; 297 | } 298 | /*< IF (Y .GE. XHUGE) THEN >*/ 299 | if (y >= xhuge) { 300 | /*< RESULT = SQRPI / Y >*/ 301 | result = sqrpi / y; 302 | /*< GO TO 300 >*/ 303 | goto L300; 304 | /*< END IF >*/ 305 | } 306 | /*< END IF >*/ 307 | } 308 | /*< YSQ = ONE / (Y * Y) >*/ 309 | ysq = one / (y * y); 310 | /*< XNUM = P(6)*YSQ >*/ 311 | xnum = p[5] * ysq; 312 | /*< XDEN = YSQ >*/ 313 | xden = ysq; 314 | /*< DO 240 I = 1, 4 >*/ 315 | for (int i__ = 1; i__ <= 4; ++i__) { 316 | /*< XNUM = (XNUM + P(I)) * YSQ >*/ 317 | xnum = (xnum + p[i__ - 1]) * ysq; 318 | /*< XDEN = (XDEN + Q(I)) * YSQ >*/ 319 | xden = (xden + q[i__ - 1]) * ysq; 320 | /*< 240 CONTINUE >*/ 321 | /* L240: */ 322 | } 323 | /*< RESULT = YSQ *(XNUM + P(5)) / (XDEN + Q(5)) >*/ 324 | result = ysq * (xnum + p[4]) / (xden + q[4]); 325 | /*< RESULT = (SQRPI - RESULT) / Y >*/ 326 | result = (sqrpi - result) / y; 327 | /*< IF (JINT .NE. 2) THEN >*/ 328 | if (jint != 2) { 329 | /*< YSQ = AINT(Y*SIXTEN)/SIXTEN >*/ 330 | double d__1 = y * sixten; 331 | ysq = d_int(d__1) / sixten; 332 | /*< DEL = (Y-YSQ)*(Y+YSQ) >*/ 333 | del = (y - ysq) * (y + ysq); 334 | /*< RESULT = EXP(-YSQ*YSQ) * EXP(-DEL) * RESULT >*/ 335 | d__1 = exp(-ysq * ysq) * exp(-del); 336 | result = d__1 * result; 337 | /*< END IF >*/ 338 | } 339 | /*< END IF >*/ 340 | } 341 | /* ------------------------------------------------------------------ */ 342 | /* Fix up for negative argument, erf, etc. */ 343 | /* ------------------------------------------------------------------ */ 344 | /*< 300 IF (JINT .EQ. 0) THEN >*/ 345 | L300: 346 | if (jint == 0) { 347 | /*< RESULT = (HALF - RESULT) + HALF >*/ 348 | result = (half - result) + half; 349 | /*< IF (X .LT. ZERO) RESULT = -RESULT >*/ 350 | if (x < zero) { 351 | result = -(result); 352 | } 353 | /*< ELSE IF (JINT .EQ. 1) THEN >*/ 354 | } else if (jint == 1) { 355 | /*< IF (X .LT. ZERO) RESULT = TWO - RESULT >*/ 356 | if (x < zero) { 357 | result = two - result; 358 | } 359 | /*< ELSE >*/ 360 | } else { 361 | /*< IF (X .LT. ZERO) THEN >*/ 362 | if (x < zero) { 363 | /*< IF (X .LT. XNEG) THEN >*/ 364 | if (x < xneg) { 365 | /*< RESULT = XINF >*/ 366 | result = xinf; 367 | /*< ELSE >*/ 368 | } else { 369 | /*< YSQ = AINT(X*SIXTEN)/SIXTEN >*/ 370 | double d__1 = x * sixten; 371 | ysq = d_int(d__1) / sixten; 372 | /*< DEL = (X-YSQ)*(X+YSQ) >*/ 373 | del = (x - ysq) * (x + ysq); 374 | /*< Y = EXP(YSQ*YSQ) * EXP(DEL) >*/ 375 | y = exp(ysq * ysq) * exp(del); 376 | /*< RESULT = (Y+Y) - RESULT >*/ 377 | result = y + y - result; 378 | /*< END IF >*/ 379 | } 380 | /*< END IF >*/ 381 | } 382 | /*< END IF >*/ 383 | } 384 | /*< 800 RETURN >*/ 385 | L800: 386 | return result; 387 | /* ---------- Last card of CALERF ---------- */ 388 | /*< END >*/ 389 | } /* calerf_ */ 390 | 391 | /* S REAL FUNCTION ERF(X) */ 392 | /*< DOUBLE PRECISION FUNCTION DERF(X) >*/ 393 | double erf_cody(double x){ 394 | /* -------------------------------------------------------------------- */ 395 | /* This subprogram computes approximate values for erf(x). */ 396 | /* (see comments heading CALERF). */ 397 | /* Author/date: W. J. Cody, January 8, 1985 */ 398 | /* -------------------------------------------------------------------- */ 399 | /*< INTEGER JINT >*/ 400 | /* S REAL X, RESULT */ 401 | /*< DOUBLE PRECISION X, RESULT >*/ 402 | /* ------------------------------------------------------------------ */ 403 | /*< JINT = 0 >*/ 404 | /*< CALL CALERF(X,RESULT,JINT) >*/ 405 | return calerf(x, 0); 406 | /* S ERF = RESULT */ 407 | /*< DERF = RESULT >*/ 408 | /*< RETURN >*/ 409 | /* ---------- Last card of DERF ---------- */ 410 | /*< END >*/ 411 | } /* derf_ */ 412 | 413 | /* S REAL FUNCTION ERFC(X) */ 414 | /*< DOUBLE PRECISION FUNCTION DERFC(X) >*/ 415 | double erfc_cody(double x) { 416 | /* -------------------------------------------------------------------- */ 417 | /* This subprogram computes approximate values for erfc(x). */ 418 | /* (see comments heading CALERF). */ 419 | /* Author/date: W. J. Cody, January 8, 1985 */ 420 | /* -------------------------------------------------------------------- */ 421 | /*< INTEGER JINT >*/ 422 | /* S REAL X, RESULT */ 423 | /*< DOUBLE PRECISION X, RESULT >*/ 424 | /* ------------------------------------------------------------------ */ 425 | /*< JINT = 1 >*/ 426 | /*< CALL CALERF(X,RESULT,JINT) >*/ 427 | return calerf(x, 1); 428 | /* S ERFC = RESULT */ 429 | /*< DERFC = RESULT >*/ 430 | /*< RETURN >*/ 431 | /* ---------- Last card of DERFC ---------- */ 432 | /*< END >*/ 433 | } /* derfc_ */ 434 | 435 | /* S REAL FUNCTION ERFCX(X) */ 436 | /*< DOUBLE PRECISION FUNCTION DERFCX(X) >*/ 437 | double erfcx_cody(double x) { 438 | /* ------------------------------------------------------------------ */ 439 | /* This subprogram computes approximate values for exp(x*x) * erfc(x). */ 440 | /* (see comments heading CALERF). */ 441 | /* Author/date: W. J. Cody, March 30, 1987 */ 442 | /* ------------------------------------------------------------------ */ 443 | /*< INTEGER JINT >*/ 444 | /* S REAL X, RESULT */ 445 | /*< DOUBLE PRECISION X, RESULT >*/ 446 | /* ------------------------------------------------------------------ */ 447 | /*< JINT = 2 >*/ 448 | /*< CALL CALERF(X,RESULT,JINT) >*/ 449 | return calerf(x, 2); 450 | /* S ERFCX = RESULT */ 451 | /*< DERFCX = RESULT >*/ 452 | /*< RETURN >*/ 453 | /* ---------- Last card of DERFCX ---------- */ 454 | /*< END >*/ 455 | } /* derfcx_ */ 456 | -------------------------------------------------------------------------------- /src/importexport.h: -------------------------------------------------------------------------------- 1 | // 2 | // This source code resides at www.jaeckel.org/LetsBeRational.7z . 3 | // 4 | // ====================================================================================== 5 | // Copyright © 2013-2014 Peter Jäckel. 6 | // 7 | // Permission to use, copy, modify, and distribute this software is freely granted, 8 | // provided that this notice is preserved. 9 | // 10 | // WARRANTY DISCLAIMER 11 | // The Software is provided "as is" without warranty of any kind, either express or implied, 12 | // including without limitation any implied warranties of condition, uninterrupted use, 13 | // merchantability, fitness for a particular purpose, or non-infringement. 14 | // ====================================================================================== 15 | // 16 | #ifndef IMPORTEXPORT_H 17 | #define IMPORTEXPORT_H 18 | 19 | #if defined(_WIN32) || defined(_WIN64) 20 | # define EXPORT __declspec(dllexport) 21 | # define IMPORT __declspec(dllimport) 22 | # else 23 | # define EXPORT 24 | # define IMPORT 25 | #endif 26 | 27 | #ifdef __cplusplus 28 | # define EXTERN_C extern "C" 29 | #else 30 | # define EXTERN_C 31 | #endif 32 | 33 | # define EXPORT_EXTERN_C EXTERN_C EXPORT 34 | # define IMPORT_EXTERN_C EXTERN_C IMPORT 35 | 36 | #endif // IMPORTEXPORT_H 37 | -------------------------------------------------------------------------------- /src/normaldistribution.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // normaldistribution.cpp 3 | // 4 | 5 | #if defined(_MSC_VER) 6 | # define NOMINMAX // to suppress MSVC's definitions of min() and max() 7 | // These four pragmas are the equivalent to /fp:fast. 8 | # pragma float_control( except, off ) 9 | # pragma float_control( precise, off ) 10 | # pragma fp_contract( on ) 11 | # pragma fenv_access( off ) 12 | #endif 13 | 14 | #include "normaldistribution.h" 15 | #include 16 | 17 | namespace { 18 | // The asymptotic expansion Φ(z) = φ(z)/|z|·[1-1/z^2+...], Abramowitz & Stegun (26.2.12), suffices for Φ(z) to have 19 | // relative accuracy of 1.64E-16 for z<=-10 with 17 terms inside the square brackets (not counting the leading 1). 20 | // This translates to a maximum of about 9 iterations below, which is competitive with a call to erfc() and never 21 | // less accurate when z<=-10. Note that, as mentioned in section 4 (and discussion of figures 2 and 3) of George 22 | // Marsaglia's article "Evaluating the Normal Distribution" (available at http://www.jstatsoft.org/v11/a05/paper), 23 | // for values of x approaching -8 and below, the error of any cumulative normal function is actually dominated by 24 | // the hardware (or compiler implementation) accuracy of exp(-x²/2) which is not reliably more than 14 digits when 25 | // x becomes large. Still, we should switch to the asymptotic only when it is beneficial to do so. 26 | const double norm_cdf_asymptotic_expansion_first_threshold = -10.0; 27 | const double norm_cdf_asymptotic_expansion_second_threshold = -1/sqrt(DBL_EPSILON); 28 | } 29 | 30 | double norm_cdf(double z){ 31 | if (z <= norm_cdf_asymptotic_expansion_first_threshold) { 32 | // Asymptotic expansion for very negative z following (26.2.12) on page 408 33 | // in M. Abramowitz and A. Stegun, Pocketbook of Mathematical Functions, ISBN 3-87144818-4. 34 | double sum = 1; 35 | if (z >= norm_cdf_asymptotic_expansion_second_threshold) { 36 | double zsqr = z * z, i = 1, g = 1, x, y, a = DBL_MAX, lasta; 37 | do { 38 | lasta = a; 39 | x = (4 * i - 3) / zsqr; 40 | y = x * ((4 * i - 1) / zsqr); 41 | a = g * (x - y); 42 | sum -= a; 43 | g *= y; 44 | ++i; 45 | a = fabs(a); 46 | } while (lasta > a && a >= fabs(sum * DBL_EPSILON)); 47 | } 48 | return -norm_pdf(z) * sum / z; 49 | } 50 | return 0.5*erfc_cody( -z*ONE_OVER_SQRT_TWO ); 51 | } 52 | 53 | double inverse_norm_cdf(double u){ 54 | // 55 | // ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3 56 | // 57 | // Produces the normal deviate Z corresponding to a given lower 58 | // tail area of u; Z is accurate to about 1 part in 10**16. 59 | // see http://lib.stat.cmu.edu/apstat/241 60 | // 61 | const double split1 = 0.425; 62 | const double split2 = 5.0; 63 | const double const1 = 0.180625; 64 | const double const2 = 1.6; 65 | 66 | // Coefficients for P close to 0.5 67 | const double A0 = 3.3871328727963666080E0; 68 | const double A1 = 1.3314166789178437745E+2; 69 | const double A2 = 1.9715909503065514427E+3; 70 | const double A3 = 1.3731693765509461125E+4; 71 | const double A4 = 4.5921953931549871457E+4; 72 | const double A5 = 6.7265770927008700853E+4; 73 | const double A6 = 3.3430575583588128105E+4; 74 | const double A7 = 2.5090809287301226727E+3; 75 | const double B1 = 4.2313330701600911252E+1; 76 | const double B2 = 6.8718700749205790830E+2; 77 | const double B3 = 5.3941960214247511077E+3; 78 | const double B4 = 2.1213794301586595867E+4; 79 | const double B5 = 3.9307895800092710610E+4; 80 | const double B6 = 2.8729085735721942674E+4; 81 | const double B7 = 5.2264952788528545610E+3; 82 | // Coefficients for P not close to 0, 0.5 or 1. 83 | const double C0 = 1.42343711074968357734E0; 84 | const double C1 = 4.63033784615654529590E0; 85 | const double C2 = 5.76949722146069140550E0; 86 | const double C3 = 3.64784832476320460504E0; 87 | const double C4 = 1.27045825245236838258E0; 88 | const double C5 = 2.41780725177450611770E-1; 89 | const double C6 = 2.27238449892691845833E-2; 90 | const double C7 = 7.74545014278341407640E-4; 91 | const double D1 = 2.05319162663775882187E0; 92 | const double D2 = 1.67638483018380384940E0; 93 | const double D3 = 6.89767334985100004550E-1; 94 | const double D4 = 1.48103976427480074590E-1; 95 | const double D5 = 1.51986665636164571966E-2; 96 | const double D6 = 5.47593808499534494600E-4; 97 | const double D7 = 1.05075007164441684324E-9; 98 | // Coefficients for P very close to 0 or 1 99 | const double E0 = 6.65790464350110377720E0; 100 | const double E1 = 5.46378491116411436990E0; 101 | const double E2 = 1.78482653991729133580E0; 102 | const double E3 = 2.96560571828504891230E-1; 103 | const double E4 = 2.65321895265761230930E-2; 104 | const double E5 = 1.24266094738807843860E-3; 105 | const double E6 = 2.71155556874348757815E-5; 106 | const double E7 = 2.01033439929228813265E-7; 107 | const double F1 = 5.99832206555887937690E-1; 108 | const double F2 = 1.36929880922735805310E-1; 109 | const double F3 = 1.48753612908506148525E-2; 110 | const double F4 = 7.86869131145613259100E-4; 111 | const double F5 = 1.84631831751005468180E-5; 112 | const double F6 = 1.42151175831644588870E-7; 113 | const double F7 = 2.04426310338993978564E-15; 114 | 115 | if (u<=0) 116 | return log(u); 117 | if (u>=1) 118 | return log(1-u); 119 | 120 | const double q = u-0.5; 121 | if (fabs(q) <= split1) 122 | { 123 | const double r = const1 - q*q; 124 | return q * (((((((A7 * r + A6) * r + A5) * r + A4) * r + A3) * r + A2) * r + A1) * r + A0) / 125 | (((((((B7 * r + B6) * r + B5) * r + B4) * r + B3) * r + B2) * r + B1) * r + 1.0); 126 | } 127 | else 128 | { 129 | double r = q<0.0 ? u : 1.0-u; 130 | r = sqrt(-log(r)); 131 | double ret; 132 | if (r < split2) 133 | { 134 | r = r - const2; 135 | ret = (((((((C7 * r + C6) * r + C5) * r + C4) * r + C3) * r + C2) * r + C1) * r + C0) / 136 | (((((((D7 * r + D6) * r + D5) * r + D4) * r + D3) * r + D2) * r + D1) * r + 1.0); 137 | } 138 | else 139 | { 140 | r = r - split2; 141 | ret = (((((((E7 * r + E6) * r + E5) * r + E4) * r + E3) * r + E2) * r + E1) * r + E0) / 142 | (((((((F7 * r + F6) * r + F5) * r + F4) * r + F3) * r + F2) * r + F1) * r + 1.0); 143 | } 144 | return q<0.0 ? -ret : ret; 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/normaldistribution.h: -------------------------------------------------------------------------------- 1 | // 2 | // This source code resides at www.jaeckel.org/LetsBeRational.7z . 3 | // 4 | // ====================================================================================== 5 | // Copyright © 2013-2014 Peter Jäckel. 6 | // 7 | // Permission to use, copy, modify, and distribute this software is freely granted, 8 | // provided that this notice is preserved. 9 | // 10 | // WARRANTY DISCLAIMER 11 | // The Software is provided "as is" without warranty of any kind, either express or implied, 12 | // including without limitation any implied warranties of condition, uninterrupted use, 13 | // merchantability, fitness for a particular purpose, or non-infringement. 14 | // ====================================================================================== 15 | // 16 | #ifndef NORMAL_DISTRIBUTION_H 17 | #define NORMAL_DISTRIBUTION_H 18 | 19 | #include 20 | #include 21 | #include "importexport.h" 22 | 23 | #define ONE_OVER_SQRT_TWO 0.7071067811865475244008443621048490392848359376887 24 | #define ONE_OVER_SQRT_TWO_PI 0.3989422804014326779399460599343818684758586311649 25 | #define SQRT_TWO_PI 2.506628274631000502415765284811045253006986740610 26 | 27 | EXPORT_EXTERN_C double erf_cody(double z); 28 | EXPORT_EXTERN_C double erfc_cody(double z); 29 | EXPORT_EXTERN_C double erfcx_cody(double z); 30 | EXPORT_EXTERN_C double norm_cdf(double z); 31 | inline double norm_pdf(double x){ return ONE_OVER_SQRT_TWO_PI*exp(-.5*x*x); } 32 | EXPORT_EXTERN_C double inverse_norm_cdf(double u); 33 | 34 | #endif // NORMAL_DISTRIBUTION_H 35 | -------------------------------------------------------------------------------- /src/rationalcubic.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // This source code resides at www.jaeckel.org/LetsBeRational.7z . 3 | // 4 | // ====================================================================================== 5 | // Copyright © 2013-2014 Peter Jäckel. 6 | // 7 | // Permission to use, copy, modify, and distribute this software is freely granted, 8 | // provided that this notice is preserved. 9 | // 10 | // WARRANTY DISCLAIMER 11 | // The Software is provided "as is" without warranty of any kind, either express or implied, 12 | // including without limitation any implied warranties of condition, uninterrupted use, 13 | // merchantability, fitness for a particular purpose, or non-infringement. 14 | // ====================================================================================== 15 | // 16 | 17 | #include "rationalcubic.h" 18 | 19 | #if defined(_MSC_VER) 20 | # define NOMINMAX // to suppress MSVC's definitions of min() and max() 21 | // These four pragmas are the equivalent to /fp:fast. 22 | // YOU NEED THESE FOR THE SAKE OF *ACCURACY* WHEN |x| IS LARGE, say, |x|>50. 23 | // This is because they effectively enable the evaluation of certain 24 | // expressions in 80 bit registers without loss of intermediate accuracy. 25 | # pragma float_control( except, off ) 26 | # pragma float_control( precise, off ) 27 | # pragma fp_contract( on ) 28 | # pragma fenv_access( off ) 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | // Based on 36 | // 37 | // “Shape preserving piecewise rational interpolation”, R. Delbourgo, J.A. Gregory - SIAM journal on scientific and statistical computing, 1985 - SIAM. 38 | // http://dspace.brunel.ac.uk/bitstream/2438/2200/1/TR_10_83.pdf [caveat emptor: there are some typographical errors in that draft version] 39 | // 40 | 41 | namespace { 42 | const double minimum_rational_cubic_control_parameter_value = -(1 - sqrt(DBL_EPSILON)); 43 | const double maximum_rational_cubic_control_parameter_value = 2 / (DBL_EPSILON * DBL_EPSILON); 44 | inline bool is_zero(double x){ return fabs(x) < DBL_MIN; } 45 | } 46 | 47 | double rational_cubic_interpolation(double x, double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double r) { 48 | const double h = (x_r - x_l); 49 | if (fabs(h)<=0) 50 | return 0.5 * (y_l + y_r); 51 | // r should be greater than -1. We do not use assert(r > -1) here in order to allow values such as NaN to be propagated as they should. 52 | const double t = (x - x_l) / h; 53 | if ( ! (r >= maximum_rational_cubic_control_parameter_value) ) { 54 | const double t = (x - x_l) / h, omt = 1 - t, t2 = t * t, omt2 = omt * omt; 55 | // Formula (2.4) divided by formula (2.5) 56 | return (y_r * t2 * t + (r * y_r - h * d_r) * t2 * omt + (r * y_l + h * d_l) * t * omt2 + y_l * omt2 * omt) / (1 + (r - 3) * t * omt); 57 | } 58 | // Linear interpolation without over-or underflow. 59 | return y_r * t + y_l * (1 - t); 60 | } 61 | 62 | double rational_cubic_control_parameter_to_fit_second_derivative_at_left_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_l) { 63 | const double h = (x_r-x_l), numerator = 0.5*h*second_derivative_l+(d_r-d_l); 64 | if (is_zero(numerator)) 65 | return 0; 66 | const double denominator = (y_r-y_l)/h-d_l; 67 | if (is_zero(denominator)) 68 | return numerator>0 ? maximum_rational_cubic_control_parameter_value : minimum_rational_cubic_control_parameter_value; 69 | return numerator/denominator; 70 | } 71 | 72 | double rational_cubic_control_parameter_to_fit_second_derivative_at_right_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_r) { 73 | const double h = (x_r-x_l), numerator = 0.5*h*second_derivative_r+(d_r-d_l); 74 | if (is_zero(numerator)) 75 | return 0; 76 | const double denominator = d_r-(y_r-y_l)/h; 77 | if (is_zero(denominator)) 78 | return numerator>0 ? maximum_rational_cubic_control_parameter_value : minimum_rational_cubic_control_parameter_value; 79 | return numerator/denominator; 80 | } 81 | 82 | double minimum_rational_cubic_control_parameter(double d_l, double d_r, double s, bool preferShapePreservationOverSmoothness) { 83 | const bool monotonic = d_l * s >= 0 && d_r * s >= 0, convex = d_l <= s && s <= d_r, concave = d_l >= s && s >= d_r; 84 | if (!monotonic && !convex && !concave) // If 3==r_non_shape_preserving_target, this means revert to standard cubic. 85 | return minimum_rational_cubic_control_parameter_value; 86 | const double d_r_m_d_l = d_r - d_l, d_r_m_s = d_r - s, s_m_d_l = s - d_l; 87 | double r1 = -DBL_MAX, r2 = r1; 88 | // If monotonicity on this interval is possible, set r1 to satisfy the monotonicity condition (3.8). 89 | if (monotonic){ 90 | if (!is_zero(s)) // (3.8), avoiding division by zero. 91 | r1 = (d_r + d_l) / s; // (3.8) 92 | else if (preferShapePreservationOverSmoothness) // If division by zero would occur, and shape preservation is preferred, set value to enforce linear interpolation. 93 | r1 = maximum_rational_cubic_control_parameter_value; // This value enforces linear interpolation. 94 | } 95 | if (convex || concave) { 96 | if (!(is_zero(s_m_d_l) || is_zero(d_r_m_s))) // (3.18), avoiding division by zero. 97 | r2 = std::max(fabs(d_r_m_d_l / d_r_m_s), fabs(d_r_m_d_l / s_m_d_l)); 98 | else if (preferShapePreservationOverSmoothness) 99 | r2 = maximum_rational_cubic_control_parameter_value; // This value enforces linear interpolation. 100 | } else if (monotonic && preferShapePreservationOverSmoothness) 101 | r2 = maximum_rational_cubic_control_parameter_value; // This enforces linear interpolation along segments that are inconsistent with the slopes on the boundaries, e.g., a perfectly horizontal segment that has negative slopes on either edge. 102 | return std::max(minimum_rational_cubic_control_parameter_value, std::max(r1, r2)); 103 | } 104 | 105 | double convex_rational_cubic_control_parameter_to_fit_second_derivative_at_left_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_l, bool preferShapePreservationOverSmoothness) { 106 | const double r = rational_cubic_control_parameter_to_fit_second_derivative_at_left_side(x_l, x_r, y_l, y_r, d_l, d_r, second_derivative_l); 107 | const double r_min = minimum_rational_cubic_control_parameter(d_l, d_r, (y_r-y_l)/(x_r-x_l), preferShapePreservationOverSmoothness); 108 | return std::max(r,r_min); 109 | } 110 | 111 | double convex_rational_cubic_control_parameter_to_fit_second_derivative_at_right_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_r, bool preferShapePreservationOverSmoothness) { 112 | const double r = rational_cubic_control_parameter_to_fit_second_derivative_at_right_side(x_l, x_r, y_l, y_r, d_l, d_r, second_derivative_r); 113 | const double r_min = minimum_rational_cubic_control_parameter(d_l, d_r, (y_r-y_l)/(x_r-x_l), preferShapePreservationOverSmoothness); 114 | return std::max(r,r_min); 115 | } 116 | -------------------------------------------------------------------------------- /src/rationalcubic.h: -------------------------------------------------------------------------------- 1 | // 2 | // This source code resides at www.jaeckel.org/LetsBeRational.7z . 3 | // 4 | // ====================================================================================== 5 | // Copyright © 2013-2014 Peter Jäckel. 6 | // 7 | // Permission to use, copy, modify, and distribute this software is freely granted, 8 | // provided that this notice is preserved. 9 | // 10 | // WARRANTY DISCLAIMER 11 | // The Software is provided "as is" without warranty of any kind, either express or implied, 12 | // including without limitation any implied warranties of condition, uninterrupted use, 13 | // merchantability, fitness for a particular purpose, or non-infringement. 14 | // ====================================================================================== 15 | // 16 | #ifndef RATIONAL_CUBIC_H 17 | #define RATIONAL_CUBIC_H 18 | 19 | // Based on 20 | // 21 | // “Shape preserving piecewise rational interpolation”, R. Delbourgo, J.A. Gregory - SIAM journal on scientific and statistical computing, 1985 - SIAM. 22 | // http://dspace.brunel.ac.uk/bitstream/2438/2200/1/TR_10_83.pdf [caveat emptor: there are some typographical errors in that draft version] 23 | // 24 | 25 | #include "importexport.h" 26 | 27 | EXPORT_EXTERN_C double rational_cubic_interpolation(double x, double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double r); 28 | EXPORT_EXTERN_C double rational_cubic_control_parameter_to_fit_second_derivative_at_left_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_l); 29 | EXPORT_EXTERN_C double rational_cubic_control_parameter_to_fit_second_derivative_at_right_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_r); 30 | EXPORT_EXTERN_C double minimum_rational_cubic_control_parameter(double d_l, double d_r, double s, bool preferShapePreservationOverSmoothness); 31 | EXPORT_EXTERN_C double convex_rational_cubic_control_parameter_to_fit_second_derivative_at_left_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_l, bool preferShapePreservationOverSmoothness); 32 | EXPORT_EXTERN_C double convex_rational_cubic_control_parameter_to_fit_second_derivative_at_right_side(double x_l, double x_r, double y_l, double y_r, double d_l, double d_r, double second_derivative_r, bool preferShapePreservationOverSmoothness); 33 | 34 | #endif // RATIONAL_CUBIC_H 35 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vollib/lets_be_rational/83ae882df8e19323798c7ebfb8898f94d2d92ade/src/version.h --------------------------------------------------------------------------------