代码之家  ›  专栏  ›  技术社区  ›  Axion004

Java-将double转换为BigDecimal时的奇数错误消息

  •  2
  • Axion004  · 技术社区  · 9 年前

    在程序中,双精度正被转换为BigDecimal。这将返回一条非常奇怪的错误消息。

    public static double riemannFuncForm(double s) {
            double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*
                (Math.sin((Math.PI*s)/2))*gamma(1-s);
    
            if(s == 1.0 || (s <= -1 && s % 2 == 0) )
                return 0;
            else if (s >= 0 && s < 2)
                return getSimpsonSum(s);
            else if (s > -1 && s < 0)
                return term*getSimpsonSum(1-s);
            else 
                return term*standardZeta(1-s);
        }
    
    BigDecimal val = BigDecimal.valueOf(riemannFuncForm(s));
    System.out.println("Value for the Zeta Function = " 
                        + val.toEngineeringString());
    

    这将返回

    Exception in thread "main" java.lang.NumberFormatException
    

    导致此错误消息的原因是什么?执行BigDecimal。valueOf(double)无法正常工作,因为它是通过另一个方法引用的?

    完整程序

    /**************************************************************************
    **
    **    Euler-Riemann Zeta Function           
    **
    **************************************************************************
    **    XXXXXXXXXX
    **    06/20/2015
    **
    **    This program computes the value for Zeta(s) using the standard form
    **    of Zeta(s), the Riemann functional equation, and the Cauchy-Schlomilch
    **    transformation. A recursive method named riemannFuncForm has been created
    **    to handle computations of Zeta(s) for s < 2. Simpson's method is
    **    used to approximate the definite integral calculated by the
    **    Cauchy-Schlomilch transformation.
    **************************************************************************/
    
    import java.util.Scanner;
    import java.math.*;
    
    public class ZetaMain {
    
        // Main method
        public static void main(String[] args) {
            ZetaMain();       
        }
    
        // Asks the user to input a value for s.
        public static void ZetaMain() {
            double s = 0;
            double start, stop, totalTime;
            Scanner scan = new Scanner(System.in);
            System.out.print("Enter the value of s inside the Riemann Zeta " + 
                    "Function: ");
            try {
                    s = scan.nextDouble();
            }
            catch (Exception e) {
                System.out.println("You must enter a positive integer greater " + 
                        "than 1.");
            }
            start = System.currentTimeMillis();
            if (s == 1)
                System.out.println("The zeta function is undefined for Re(s) " +
                        "= 1.");
            else if (s < 2) {
                BigDecimal val = BigDecimal.valueOf(riemannFuncForm(s));
                System.out.println("Value for the Zeta Function = " 
                        + val.toEngineeringString());
            }
            else 
                System.out.println("Value for the Zeta Function = " 
                        + BigDecimal.valueOf(getStandardSum(s)).toString());
            stop = System.currentTimeMillis();
            totalTime = (double) (stop-start) / 1000.0;
            System.out.println("Total time taken is " + totalTime + " seconds.");
    
        }
    
        // Standard form of the Zeta function.
        public static double standardZeta(double s) {
            int n = 1;
            double currentSum = 0;
            double relativeError = 1;
            double error = 0.000001;
            double remainder;
    
            while (relativeError > error) {
                currentSum = Math.pow(n, -s) + currentSum;
                remainder = 1 / ((s-1)* Math.pow(n, (s-1)));
                relativeError =  remainder / currentSum;
                n++;
            }
            System.out.println("The number of terms summed was " + n + ".");
            return currentSum;
        }
    
        // Returns the value calculated by the Standard form of the Zeta function.
        public static double getStandardSum(double s){
            return standardZeta(s);
        }
    
        // Approximation of the Gamma function through the Lanczos Approximation.
        public static double gamma(double s){
                        double[] p = {0.99999999999980993, 676.5203681218851, 
                            -1259.1392167224028, 771.32342877765313, 
                            -176.61502916214059, 12.507343278686905,
                            -0.13857109526572012, 9.9843695780195716e-6, 
                            1.5056327351493116e-7};
    
                        int g = 7;
    
                        // Implements Euler's Reflection Formula.
                        if(s < 0.5) return Math.PI / (Math.sin(Math.PI * s)
                                *gamma(1-s));
    
                        s -= 1;
                        double a = p[0];
                        double t = s + g + 0.5;
                        for(int i = 1; i < p.length; i++){
                                a += p[i] / (s+i);
                        }
    
                        return Math.sqrt(2*Math.PI)*Math.pow(t, s+0.5)
                                *Math.exp(-t)*a;
                }
    
        /* Riemann's Functional Equation - Directly calculates the value of 
            Zeta(s) for s < 2.
    
            1. The first if statement handles the case when s < 0 and s is a 
                multiple of 2k. These are trivial zeroes where Zeta(s) is 0.
    
            2. The second if statement handles the values of 0 < s < 2. Simpson's 
               method is used to numerically compute an approximation of the 
               definite integral.
    
            3. The third if statement handles the values of -1 < s < 0. Recursion 
               is used alongside an approximation through Simpson's method.
    
            4. The last if statement handles the case for s <= -1 and is not a 
               trivial zero. Recursion is used directly against the standard form 
               of Zeta(s).
        */
        public static double riemannFuncForm(double s) {
            double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*
                (Math.sin((Math.PI*s)/2))*gamma(1-s);
    
            if(s == 1.0 || (s <= -1 && s % 2 == 0) )
                return 0;
            else if (s >= 0 && s < 2)
                return getSimpsonSum(s);
            else if (s > -1 && s < 0)
                return term*getSimpsonSum(1-s);
            else 
                return term*standardZeta(1-s);
        }
    
        // Returns the function referenced inside the right hand side of the 
        // Cauchy-Schlomilch transformation for Zeta(s).
        public static double function(double x, double s) {
            double sech = 1 / Math.cosh(x); // Hyperbolic cosecant
            double squared = Math.pow(sech, 2);
            return ((Math.pow(x, s)) * squared);
        }
    
     // Simpson's rule - Approximates the definite integral of f from a to b.
        public static double SimpsonsRule(double a, double b, double s, int n) {
            double simpson, dx, x, sum4x, sum2x;
    
            dx = (b-a) / n;
            sum4x = 0.0;
            sum2x = 0.0;
    
            // 4/3 terms
            for (int i = 1; i < n; i += 2) {
                x = a + i * dx;
                sum4x += function(x,s);
            }
    
            // 2/3 terms
            for (int i = 2; i < n-1; i += 2) {
                x = a + i * dx;
                sum2x += function(x,s);
            }
    
            // Compute the integral approximation.
            simpson = function(a,s) + function(a,b);
            simpson = (dx / 3)*(simpson + 4 * sum4x + 2 * sum2x);
            return simpson;
        }
    
        // Handles the error for for f(x) = t^s * sech(t)^2. The integration is
        // done from 0 to 100.
        // Stop Simspson's Method when the relative error is less than 1 * 10^-6
        public static double SimpsonError(double a, double b, double s, int n)
        {
            double futureVal;
            double absError = 1.0;
            double finalValueOfN;
            double numberOfIterations = 0.0;
            double currentVal = SimpsonsRule(a,b,s,n);
    
            while (absError / currentVal > 0.000001) {
                n = 2*n;
                futureVal = SimpsonsRule(a,b,s,n);
                absError = Math.abs(futureVal - currentVal) / 15;
                currentVal = futureVal;
            }
    
            // Find the number of iterations. N starts at 8 and doubles 
            // every iteration.
            finalValueOfN = n / 8;
            while (finalValueOfN % 2 == 0) {
                finalValueOfN = finalValueOfN / 2;
                numberOfIterations++;
            }
            System.out.println("The number of iterations is " 
                    + numberOfIterations + ".");
            return currentVal;
        }
    
    
        // Returns an approximate sum of Zeta(s) through Simpson's rule.
        public static double getSimpsonSum(double s) {
            double constant = Math.pow(2, (2*s)-1) / (((Math.pow(2, s)) -2)*
                    (gamma(1+s)));
            System.out.println("Did Simpson's Method.");
            return constant*SimpsonError(0, 100, s, 8);
        }
    }
    
    2 回复  |  直到 9 年前
        1
  •  3
  •   Stephen C    9 年前

    为了解决这个问题,我是否必须将所有双精度计算更改为BigDecimal计算?

    不。您需要做的就是捕获并适当处理NumberFormatException。或者,测试 NaN Inf 在尝试转换 double .

    在这种情况下,您只使用 BigDecimal 用于“工程”语法格式。因此,另一种选择是直接进行格式化。(尽管我还没有找到一个简单的方法。)

        2
  •  3
  •   Community CDub    7 年前

    发生此错误是因为BigDecimal。valueOf(value)不接受“NaN”“不是数字”作为参数,以下表达式将返回NaN

    Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s)
    

    Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2)) 将求值为-0.0

    和这个函数 gamma(1-s) 将评估“无限”

    在java中So-0.0*无限等于NaN

    请看这个,了解Java何时可以生成NaN。

    When can Java produce a NaN?