Search code examples
javaparsingdoubleargs

Method working only with < than 5000 args parameters


I have a small problem with a method to convert a string like "n.n+, n.n+, n.n+, ..., n.n+ ." in a sequence of double. Everything works perfectly fine if I have lesse then 4998 values. If I have 4998 or more, the following exception is thrown:

Exception in thread "main" java.lang.NumberFormatException: For input string: "1.178293466734042,"
        at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
        at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
        at java.base/java.lang.Double.parseDouble(Double.java:543)
        at java.base/java.lang.Double.valueOf(Double.java:506)

Here is the method I have written:

private static void parseDoubleArrayFromInput (String[] input, double[] parsedDoubles){
        for(int i = 0 ; i < input.length - 1; i++){                     //Removes the last character, which is the comma, apart from the last valid string.
            if(i == input.length - 2)                                   //Does not consider the last string of the array since it contains only the dot.
                parsedDoubles[i] = Double.parseDouble(input[i]);        //Then, it fills the array of doubles with the parsed double.
            else
                parsedDoubles[i] = Double.parseDouble(input[i].substring(0,input[i].length()-1));
        }
    }

This is the class that generates the files

import java.time.*;

public class PRNG {

    public PRNG(){

    }

    public static String generatePseudoRandomNumbers(double actual, int repetitions) {        //main function, generates pseudo random numbers and
        String output = "";
        double[] values = new double[repetitions];
        for(int i = 0; i < repetitions; i++) {
            double nextNumber = Math.pow(7,5)* actual % (Math.pow(2,31)- 1);    //algorithm to generate next pseudo random number
            actual = nextNumber;
            double singleResult = (2*(actual / (Math.pow(2,31) - 2))); //equation to shrink random numbers in a little range (0..100 in this case)
            output = output + singleResult + ", ";
        }
        output = output.substring(0, output.length()-3) + " .";
        return output;
    }

    public static int generatePseudoRandomNumbersTimes(double actual) {
        double nextNumber = Math.pow(7,5)* actual % (Math.pow(2,31)- 1);
        int singleResult = (int)(21*(nextNumber / (Math.pow(2,31) - 2)));
        return singleResult;
    }
}

And this is the class that should receive the input and parse it in a sequence of doubles

import java.util.Arrays;

public class StupidAlg {

    public static void main (String[] args){
        double[] doubleInput;
        if (args.length ==0) {
            System.out.print("");
        }
        else {
            doubleInput = new double[args.length - 1];
            parseDoubleArrayFromInput(args, doubleInput);
            double target = sum(doubleInput) / 2;                   //find target value
            double value = findWeightedMedian(doubleInput, target);
            System.out.print(value);
        }
    }

    private static void parseDoubleArrayFromInput (String[] input, double[] parsedDoubles){
        // process all of the input
        for(int i = 0 ; i < input.length - 1; i++) {
            // remove from the input things that will break the parsing
            // NOTE: other approaches could be used to ensure there is
            //   only a single ".".
            // NOTE: assumes the input to be US standard, as other approachs
            //        might use a "," for separator
            String clean = input[i].replaceAll("[^0-9.]", "");

            // put the result
            parsedDoubles[i] = Double.parseDouble(clean);
        }
    }

    private static double findWeightedMedian (double[] input, double target){
        Arrays.sort(input);
        double sum = 0;
        double result = 0;
        for(double v : input){
            sum += v;
            if (sum >= target) {
                result = v;
                break;
            }
        }
        return result;
    }

    private static double sum (double[] input){
        double result = 0;
        for(double v : input){
            result = result + v;     //sum all values in array
        }
        return result;
    }
}

I used the library Arrays just because I didn't want to implement the sorting algorithm since it's an easy problem and this is the test file.

The input files are made this way

0.2931308777007562, 0.650659222761783, 1.6295518657467811, 1.8781948535500046, 0.8208889158637159, 0.680002497211101, 0.8019653053972547, 0.6308815354768946, 1.2259618232268485, 0.7403533791567696, 1.1192376940690332, 1.0279154591522324, 0.1751139268047306, 1.139766437131694, 0.05449995217332612, 1.9806957514776808, 1.5534795844494176, 1.3313636838750575, 0.22942446845530018, 1.937039533571377, 1.8234255749950423, 0.31362467102112684, 1.08984339804374, 0.9979823920856997, 1.090055974284239, 0.570751264291583 .

Solution

  • The exception is pretty clear -- the Double.parseDouble() (actually, all of the Integer/Long/etc. .parseXXX() methods) will throw if the input String is not in the expected format.

    So, it is best to:

    • Catch the exception, or know that it can propagate
    • Check the validity of the input before attempting to parse

    The current algorithm assumes the location of the bad value. This approach seems very fragile. I would clean the input, and allow the exception to propagate.

    private static void parseDoubleArrayFromInput (String[] input, double[] parsedDoubles){
        // process all of the input
        for(int i = 0 ; i < input.length; i++) {
            // remove from the input things that will break the parsing
            // NOTE: other approaches could be used to ensure there is 
            //   only a single ".".
            // NOTE: assumes the input to be US standard, as other approachs
            //        might use a "," for separator
            String clean = input[i].replaceAll("[^0-9.]", "");
    
            // put the result
            parsedDoubles[i] = Double.parseDouble(clean);
        }
    }