Search code examples
javadoublebigdecimal

Java - Odd error message when converting double to BigDecimal


In a program, a double is being converted to BigDecimal. This returns a very strange error message.

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());

This returns

Exception in thread "main" java.lang.NumberFormatException

What is causing this error message? Does BigDecimal.valueOf(double) not work correctly since this is referenced through another method?

Full program

/**************************************************************************
**
**    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);
    }
}

Solution

  • Would I have to change all of my double calculations to BigDecimal calculations in order to fix this?

    Nope. All you would need to do is to catch and handle the NumberFormatException appropriately. Or, test for NaN and Inf before attempting to convert the double.

    In this case, you are only using BigDecimal for formatting in "engineering" syntax. So another alternative would be to do the formatting directly. (Though I haven't found a simple way to do that yet.)