2, 数学处理函数, 包括exp, ln, pow.
package cn.cublog.websurf.utils;
public class FloatEx
{
//Compare two double number.
static int compare(double a1, double a2)
{
final double EQUAL_ERROR = 1.0E-20;
if(a1 < a2 - EQUAL_ERROR)
{
return -1;
}
else if(a1 > a2 + EQUAL_ERROR)
{
return 1;
}
else
{
return 0;
}
}
//The Maclaurin expanding equation is:
// e^x = 1 + x + x^2/2! + x^3/3! + x^3/3! + ... + x^n/n! (-∞
//Suppose:
// e^x = m(0) + m(1) + m(2) + m(3) + ... + m(n)
//Then:
// m(n) = m(n-1) * x / n (n >= 1)
static public double exp(double x)
{
double lastItem = 1;
double result = 1;
double lastResult;
int i;
for(i = 1; true; i++)
{
lastResult = result;
lastItem = (lastItem * x) / i;
result += lastItem;
if(result == Double.NaN ||
result == Double.NEGATIVE_INFINITY ||
result == Double.POSITIVE_INFINITY ||
compare(result, lastResult) == 0)
{
//System.out.println(" result = " + result);
//System.out.println("lastResult = " + lastResult);
break;
}
}
//System.out.println("Ileration times is: " + i);
return result;
}
//The definatinon of Logarithm function of logarithm is:
//y = loga(x); ( 0 < x < +∞; a > 0, a != 1; -∞ < y < +∞)
//
//The Maclaurin expanding equation is:
// ln(1+x)= x - x^2/2 + x^3/3 - x^4/4 + ...+ (-1)^(n+1)*x^(n)/(n); (-1 //
//Suppose: t = 1 + x
//Thus:
// x = t - 1; (0 < t <= 2)
// ln(t) = (t-1) - (t-1)^2/2 + (t-1)^3/3 - (t-1)^4/4 + ... + (-1)^(n+1)*(t-1)^n/n; (0 < t <= 2)
//Suppose:
// ln(t) = a(0)*m(0) + a(1)*m(1)/2 + a(2)*m(2)/3 + ... + a(n)*m(n)/(n+1);
//Then:
// a(0) = 1;
// a(n) = -1 * a(n - 1); (n >= 1)
// m(n) = m(n - 1) * x; (n >= 1)
//
//So:, the result of ln(t) is:
//(1), if t <=0, it is NAN;
//(2), if 0 < t <= 2, using Maclaurin expanding equation to get result;
//(3), if t > 2, 0 < 1/t< 1/2; suppose m = 1/t (0 < t)
// ln(t) = ln(t)^(-1 * -1) = -1 * ln(t)^(-1) = -ln(1/t); (t > 2, 0 < 1/t < 1/2),
// Now, we can use Maclaurin expanding equation to get result.
//Caution:
// The convergent prosess of this method is very very slow when t is close to 0, 1, 2, and 0 //The iterative times is larger than 100000;
static public double ln(double t)
{
boolean isReciprocal = false;
double sign = -1;
double lastItem = 1;
double result = 0;
double lastResult;
double x;
int i;
//Invalid t.
if(compare(t, 0.0) <= 0)
{
return Double.NaN;
}
//For special t, return its result directly to speed up calculating.
if(compare(t, 1.0) == 0)
{
return 0.0;
}
if(compare(t, 2.0) > 0)
{
t = 1.0/t; //Set the value of t in (0, 2]; now 0 < t < 1/2;
isReciprocal = true;
}
x = t - 1;
for(i = 1; true; i++)
{
lastResult = result;
sign *= -1;
lastItem *= x;
result += (sign * lastItem) / i;
if(result == Double.NaN ||
result == Double.NEGATIVE_INFINITY ||
result == Double.POSITIVE_INFINITY ||
compare(result, lastResult) == 0)
{
break;
}
}
if(isReciprocal)
{
result = -1 * result;
}
return result;
}
//Speed up calculating.
//Another Maclaurin expanding equation of logarithm is:
// ln(1-x) = -x - x^2/2 - x^3/3 - x^4/4 - ... - x^n/n; (-1 <=x < 1)
//Thus:
// ln((1+x)/(1-x)) = ln(1+x) - ln(1-x) = 2(x + x^3/3 + x^5/5 + ... + x^(2n+1)/(2n+1); ( -1 < x < 1)
//
//Suppose:
// t = (1+x)/(1-x); (0 < t < +∞)
//Then:
// x = (t-1)/(t+1)
//Caution:
// if t is in the range (0.0, 0.5), the convergent prosess is still slow.
static public double ln2(double t)
{
double lastDenominator;
double lastmolecule;
double result;
double lastResult;
double x;
int i;
if(compare(t, 0.0) <= 0)
{
return Double.NaN;
}
if(compare(t, 1.0) == 0)
{
return 0.0;
}
x = (t - 1)/(t + 1);
lastmolecule = x;
lastDenominator = 1;
result = 2 * x;
for(i = 1; true; i++)
{
lastResult = result;
lastmolecule = lastmolecule * x * x;
lastDenominator += 2;
result += (2 * lastmolecule) / lastDenominator;
if(result == Double.NaN ||
result == Double.NEGATIVE_INFINITY ||
result == Double.POSITIVE_INFINITY ||
compare(result, lastResult) == 0)
{
break;
}
}
//System.out.println("Iteration times is: " + i);
return result;
}
//When t is in the range (0.0, 0.5), the convergent process of ln(t) is too slow.
//if map "t" from (0.0, 0.5) to (1.0, 2.0], it will become fast.
//This is best method to caltulate ln(t);
static public double ln3(double t)
{
double result;
if(compare(t, 0.0) <= 0)
{
return Double.NaN;
}
if(compare(t, 1.0) == 0)
{
return 0.0;
}
if(compare(t, 0.5) < 0)
{
//Suppose:
// t = a * e^n (n is float point number)
//Then:
// y = ln(t) = ln(a*e^n) = ln(a) + ln(e^n) = ln(a) + n;
//Thus, if "a" is larger than 1, and less than or equal to 2, "t" is mapped from (0.0, 0.5) to
//(1.0, 2.0], the convergent process of ln(a) is faster than ln(t).
//
//The left task is to find proper "n" to make:
// (1) t = a * e^n
// (2) "a" in the range (1, 2];
//
//"a" is in the range(1, 2], so a*e^n is in the range (e^n, 2*e^n]; After specify "n",
// if "t" is in the range (e^n, 2*e^n], that's OK, we calculate "a" using a = t / e^n;
//finally according to ln(t) = ln(a) + n to get result;
// if "t" is less than the minimum of e^n, we need decrease some value from n, suppose it's d,
//but how much is d? In onder to map successfully, (e^n, 2*e^n] must be continuous, so the union
//of (e^(n-d), 2*e^(n-d)] and (e^n, 2*e^n] is not null, thus 2 * e^(n-d) >= e^n ==> d <= ln2;
//
//But how to set beginning value of "n"? we know, if n == -1, a*e^n is in(0.3678794,0.7357588],
//the maximum value is larger than 0.5, and the minimum value is less than 0.5, so we set
//beginning value of "n" to -1,
/*
//t = a*e^n
//
//ln2 = 0.69314718055994530941723212145818;
//d <= ln2;
//final double STEP = 0.69314718055994530941;
final double STEP = 0.01;
double n = -1;
double a = t / exp(n);
while(compare(a, 1.0) < 0)
{
n = n - STEP;
a = t / exp(n);
}
System.out.println("n=" + n + " a=" + a);
System.out.println("t=" + a * exp(n));
result = ln2(a) + n;
*/
//Above method has a critical problem, "a" is calculated from t and e^n, if we use exp(...) defined
//above to get e^n, the result of exp(...) has error, so the result of ln will be influenced by exp(...).
//Thus we consider to use other method which don't use exp;
//
//Suppose:
// x = t * m^n (m is constant; 0.0 < t <= 0.5; 1.0 < x <= 2.0);
// So:
// t = x * m^(-n);
//
//t is given from argument list, We only need find proper "n" to let x is in the range (1.0, 2.0];
//Then get result using:
// ln(x) = ln(t * m^n) = ln(t) + n*ln(m) ==> ln(t) = ln(x) - n*ln(m)
//
//How to specify m:
// x = t * m^n ==> t = x * m^(-n)
// 1.0 < x <= 2.0 ==> x*m^(-n) is in (m^(-n), 2 * m^(-n)],
//In order to make (m^(-n), 2 * m^(-n)] is continuous, following equivalent must be true:
// 1 * m^(-n) <= 2 * m^(-(n+1)) <= 2 * m^(-n) ==> 1 < m <= 2
//
//if m is 2, x = t * 2^n ==> ln(x) = ln(t) + n*ln(2) ==> ln(t) = ln(x) - n*ln(2);
//
//if m = 1.01, 1.5, 2.0, the result error is less than 1E-12;
final double LN2 = 0.69314718055994530941723212145818; //ln(2), m = 2.0
//final double LN1_5 = 0.405465108108164381; //ln(1.5),m = 1.5
//final double LN1_01 = 0.00995033085316808; //ln(1.01), m = 1.01
double x = t;
double n = 0;
int i;
while(compare(x, 1.0) < 0)
{
x = x * 2.0;
n++;
}
result = ln2(x) - n * LN2;
}
else if(compare(t, 2.0) > 0)
{
result = -1 * ln3(1.0/t);
}
else
{
result = ln2(t);
}
return result;
}
//The defination of exponential power function is:
// t = x^y
//Suppose:
// ln(x^y) = ln(x^y)
// ==> x^y = e^(ln(x^y))
// ==> x^y = e^(y * ln(x))
static public double pow(double x, double y)
{
double floor;
if(compare(x, 1.0) == 0 || compare(y, 0.0) == 0)
{
return 1.0;
}
else if(compare(x, 0.0) == 0)
{
return 0.0;
}
else if(compare(y, 1.0) == 0)
{
return x;
}
floor = Math.floor(y);
if(compare(floor, y) == 0)
{
//if y is integer, calculate directly to speed up process.
int i;
double result = 1;
boolean yIsNegative = false;
if(compare(y, 0.0) < 0)
{
yIsNegative = true;
floor = Math.abs(floor);
}
for(i = 0; i < floor; i++)
{
result *= x;
}
if(yIsNegative)
{
result = 1.0/result;
}
return result;
}
else
{
return exp(y * ln3(x));
}
}
}
参考文献:
1, 初等数学中关于基本初等函数部分。
2, 数学分析中关于泰勒展开式及麦克劳林展开式部分。
3, 。
4, 实现log()和exp函数的方法,并以此计算pow
5,
注:
1, 欢迎讨论、拍砖。
2, 本文中的代码可以任意使用、修改, 但请在源码中注明来源(http://websurf.cublog.cn/)。
3, 本文中的代码没有做完整的测试, 本人不作任何担保; 如果这些代码给你带来任何形式的影响,一概与本人无关。
4, 转载本文请注明出处(http://websurf.cublog.cn/)。
阅读(1622) | 评论(0) | 转发(0) |