29#include <boost/math/special_functions/sign.hpp>
33 namespace exponential_integrals_helper {
40 const Real x2 = 1.0/(x*x);
43 1 + x2*(7.44437068161936700618e2 + x2*(1.96396372895146869801e5
44 + x2*(2.37750310125431834034e7 + x2*(1.43073403821274636888e9
45 + x2*(4.33736238870432522765e10 + x2*(6.40533830574022022911e11
46 + x2*(4.20968180571076940208e12 + x2*(1.00795182980368574617e13
47 + x2*(4.94816688199951963482e12 - x2*4.94701168645415959931e11)))))))))
49 1 + x2*(7.46437068161927678031e2 + x2*(1.97865247031583951450e5
50 + x2*(2.41535670165126845144e7 + x2*(1.47478952192985464958e9
51 + x2*(4.58595115847765779830e10 + x2*(7.08501308149515401563e11
52 + x2*(5.06084464593475076774e12 + x2*(1.43468549171581016479e13
53 + x2*1.11535493509914254097e13))))))))
58 const Real x2 = 1.0/(x*x);
61 1 + x2*(8.1359520115168615e2 + x2*(2.35239181626478200e5
62 + x2*(3.12557570795778731e7 + x2*(2.06297595146763354e9
63 + x2*(6.83052205423625007e10 + x2*(1.09049528450362786e12
64 + x2*(7.57664583257834349e12 + x2*(1.81004487464664575e13
65 + x2*(6.43291613143049485e12 - x2*1.36517137670871689e12)))))))))
67 1 + x2*(8.19595201151451564e2 + x2*(2.40036752835578777e5
68 + x2*(3.26026661647090822e7 + x2*(2.23355543278099360e9
69 + x2*(7.87465017341829930e10 + x2*(1.39866710696414565e12
70 + x2*(1.17164723371736605e13 + x2*(4.01839087307656620e13
71 + x2*3.99653257887490811e13))))))))
76 namespace ExponentialIntegral {
84 ( 1 + x2*(-4.54393409816329991e-2 + x2*(1.15457225751016682e-3
85 + x2*(-1.41018536821330254e-5 + x2*(9.43280809438713025e-8
86 + x2*(-3.53201978997168357e-10 + x2*(7.08240282274875911e-13
87 - x2*6.05338212010422477e-16))))))
89 1 + x2*(1.01162145739225565e-2 + x2*(4.99175116169755106e-5
90 + x2*(1.55654986308745614e-7 + x2*(3.28067571055789734e-10
91 + x2*(4.5049097575386581e-13 + x2*3.21107051193712168e-16)))))
95 using namespace exponential_integrals_helper;
96 return M_PI_2 -
f(x)*std::cos(x) - g(x)*std::sin(x);
101 QL_REQUIRE(x >= 0,
"x < 0 => Ci(x) = Ci(-x) + i*pi");
107 x2* ( -0.25 + x2*(7.51851524438898291e-3 +x2*(-1.27528342240267686e-4
108 + x2*(1.05297363846239184e-6 +x2*(-4.68889508144848019e-9
109 + x2*(1.06480802891189243e-11 - x2*9.93728488857585407e-15)))))
111 1 + x2*(1.1592605689110735e-2 + x2*(6.72126800814254432e-5
112 + x2*(2.55533277086129636e-7 + x2*(6.97071295760958946e-10
113 + x2*(1.38536352772778619e-12 + x2*(1.89106054713059759e-15
114 + x2*1.39759616731376855e-18))))))
118 using namespace exponential_integrals_helper;
119 return f(x)*std::sin(x) - g(x)*std::cos(x);
123 std::complex<Real>
Ei(
124 const std::complex<Real>& z,
const std::complex<Real>& acc) {
126 if (z.real() == 0.0 && z.imag() == 0.0
127 && std::numeric_limits<Real>::has_infinity) {
128 return std::complex<Real>(-std::numeric_limits<Real>::infinity());
132 constexpr double DIST = 4.5;
133 constexpr double MAX_ERROR = 5.0 *
QL_EPSILON;
136 QL_REQUIRE(z.real() < z_inf,
"argument error " << z);
138 const Real z_asym = 2.0 - 1.035*std::log(MAX_ERROR);
140 using boost::math::sign;
141 const Real abs_z = std::abs(z);
143 const auto match = [=](
144 const std::complex<Real>& z1,
const std::complex<Real>& z2)
146 const std::complex<Real>
d = z1 - z2;
147 return std::abs(
d.real()) <= MAX_ERROR*std::abs(z1.real())
148 && std::abs(
d.imag()) <= MAX_ERROR*std::abs(z1.imag());
151 if (z.real() > z_inf)
152 return std::complex<Real>(std::exp(z)/z) + acc;
154 if (abs_z > 1.1*z_asym) {
155 std::complex<Real> ei = acc + std::complex<Real>(
Real(0.0), sign(z.imag())*
M_PI);
156 std::complex<Real>
s(std::exp(z)/z);
157 for (
Size i=1; i <= std::floor(abs_z)+1; ++i) {
163 QL_FAIL(
"series conversion issue for Ei(" << z <<
")");
166 if (abs_z > DIST && (z.real() < 0 || std::abs(z.imag()) > DIST)) {
167 std::complex<Real> ei(0.0);
168 for (
Size k = 47; k >=1; --k) {
169 ei = -
Real(k*k)/(2.0*k + 1.0 - z + ei);
171 return (acc + std::complex<Real>(0.0, sign(z.imag())*
M_PI))
172 - std::exp(z)/ (1.0 - z + ei);
174 QL_FAIL(
"series conversion issue for Ei(" << z <<
")");
177 std::complex<Real>
s(0.0), sn=z;
181 for (
n=2;
n < 1000 &&
s+sn*nn !=
s; ++
n) {
185 nn += 1/(2.0*(
n/2) + 1);
187 sn *= -z /
Real(2*
n);
190 QL_REQUIRE(
n < 1000,
"series conversion issue for Ei(" << z <<
")");
192 const std::complex<Real>
r
195 if (z.imag() !=
Real(0.0))
198 return std::complex<Real>(
r.real(), acc.imag());
201 std::complex<Real>
Ei(
const std::complex<Real>&z) {
202 return Ei(z, std::complex<Real>(0.0));
205 std::complex<Real>
E1(
const std::complex<Real>& z) {
206 if (z.imag() < 0.0) {
207 return -
Ei(-z, std::complex<Real>(0.0, -
M_PI));
209 else if (z.imag() > 0.0 || z.real() < 0.0) {
210 return -
Ei(-z, std::complex<Real>(0.0,
M_PI));
219 std::complex<Real>
Si(
const std::complex<Real>& z) {
220 if (std::abs(z) <= 0.2) {
221 std::complex<Real>
s(0), nn(z);
223 for (k=2; k < 100 &&
s !=
s+nn; ++k) {
225 nn *= -z*z/((2.0*k-2)*(2*k-1)*(2*k-1))*(2.0*k-3);
227 QL_REQUIRE(k < 100,
"series conversion issue for Si(" << z <<
")");
232 const std::complex<Real> i(0.0, 1.0);
233 return 0.5*i*(
E1(-i*z) -
E1(i*z)
234 - std::complex<Real>(0.0, ((z.real() >= 0 && z.imag() >= 0)
235 || (z.real() > 0 && z.imag() < 0) )?
M_PI : -
M_PI));
239 std::complex<Real>
Ci(
const std::complex<Real>& z) {
240 const std::complex<Real> i(0.0, 1.0);
242 std::complex<Real> acc(0.0);
243 if (z.real() < 0.0 && z.imag() >= 0.0)
245 else if (z.real() <= 0.0 && z.imag() <= 0.0)
248 return -0.5*(
E1(-i*z) +
E1(i*z)) + acc;
floating-point comparisons
Classes and functions for error handling.
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
#define QL_FAIL(message)
throw an error (possibly with file and line information)
#define M_EULER_MASCHERONI
std::size_t Size
size of a container
std::complex< Real > Ei(const std::complex< Real > &z, const std::complex< Real > &acc)
std::complex< Real > E1(const std::complex< Real > &z)
ext::shared_ptr< YieldTermStructure > r