Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2087547
  • 博文数量: 413
  • 博客积分: 10926
  • 博客等级: 上将
  • 技术积分: 3862
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-09 18:14
文章分类

全部博文(413)

文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(6)

2011年(138)

2010年(85)

2009年(42)

2008年(46)

2007年(26)

2006年(59)

分类: Java

2007-05-06 13:25:03

 
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/)。


阅读(1615) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~