Search code examples
androidnumberformatexception

Double.parseFloat odd behavior on higher number values


I have a piece of code that takes a number value entered in an EditText field, parses it into a Double, and adds or removes it from another Double value (the sum). This works fine most of the time, except for when the entered value (or the value it's added to) goes higher or lower than 1000 or -1000, respectively.

There's a few caveats here: The button the user presses to add the value has a check on it to see if the EditText field is blank when the button is pressed; if so, it does nothing, and tells the user through a Toast that they need to enter a non-blank value. On the other hand, if the user inputs a value (let's say 999.99 or less), it will clear the EditText field, add the value to the earlier-mentioned sum, and also places the entered value into a ListView adapter.

Now for the odd behavior: if the user enters a value of 1000 or above, regardless of whatever the sum equals (so for example: a sum of 500 minus 1000 still replicates this issue), the amount is added to the ListView, but the sum isn't changed, the EditText keeps the value entered (and appends a minus symbol to the front if the value was subtracted from the sum), and the user is notified that they need to enter a non-blank value.

Normally, clicking on an item in the ListView removes it from the list and adds or removes that amount to/from the sum. If this is attempted on a value 1000 or over, the app crashes with a NumberFormatException citing an "invalid Double".

I'll post the code that handles the above actions below, as well as the LogCat. If anyone can help me tackle this problem I"d greatly appreciate it, as it's the only thing stopping me from releasing my app to the public. Thank you!

/**
     * Method to handle ListView clicks. It should ask the user if they want to
     * remove the clicked item, and on confirmation, should do so.
     */
    @Override
    protected void onListItemClick(ListView l, View v, final int position, long id){
        final String item = (String) getListAdapter().getItem(position).toString();
        final TextView allowance = (TextView) findViewById(R.id.main_textview_allowance);

        // Offer up a dialog window to ask if the user really wants to delete
        // the clicked item.
        AlertDialog.Builder deleteDialog = new AlertDialog.Builder(this);
        deleteDialog.setTitle("Confirmation");
        deleteDialog.setMessage("Are you sure you want to delete " + item + "?");
        deleteDialog.setPositiveButton("OK", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface arg0, int arg1){
                // Delete the item
                adapter.remove(item);
                adapter.notifyDataSetChanged();

                // We need to remove the deleted value from our allowance
                double subtraction = Double.parseDouble(item.toString());
                double newValue = Double.parseDouble(allowance.getText().toString()) - subtraction;
                allowance.setText(moneyFormat.format(newValue));

                Toast.makeText(MainActivity.this, item + " transaction deleted.", Toast.LENGTH_LONG).show();
            }
        });
        deleteDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface arg0, int arg1){
                // Do nothing
            }
        });
        deleteDialog.show();
    }// onListItemClick

...

/**
     * Add an expense to the ListView
     * @param view The calling View object.
     */
    public void addExpense(View view){
        EditText expense = (EditText)findViewById(R.id.main_edittext_expenseentry);
        TextView allowance  = (TextView) findViewById(R.id.main_textview_allowance);

        // Invert the value given such that it subtracts the expense from the allowance,
        // not add to it. But first, if the field is blank, don't do anything and let
        // the user know they need to input a value first.
        try{
            double expenseValue = Double.parseDouble(expense.getText().toString()) * -1;

            expense.setText(moneyFormat.format(expenseValue));
            adapter.add(expense.getText().toString());
            adapter.notifyDataSetChanged();

            // We need to subtract the new value from our daily allowance
            double subtraction = Double.parseDouble(expense.getText().toString());
            double newValue = (Double.parseDouble(allowance.getText().toString())) + subtraction;
            allowance.setText(moneyFormat.format(newValue));
            expense.setText("");
        }catch(NumberFormatException e){
            Toast.makeText(this, "You need to enter a non-blank amount!", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Add a credit to the ListView
     * @param view The calling View object.
     */
    public void addCredit(View view){
        EditText expense = (EditText)findViewById(R.id.main_edittext_expenseentry);
        TextView allowance  = (TextView) findViewById(R.id.main_textview_allowance);

        // Invert the value given such that it subtracts the expense from the allowance,
        // not add to it. But first, if the field is blank, don't do anything and let
        // the user know they need to input a value first.
        try{
            double expenseValue = Double.parseDouble(expense.getText().toString());

            expense.setText(moneyFormat.format(expenseValue));
            adapter.add(expense.getText().toString());
            adapter.notifyDataSetChanged();

            // We need to add the new value to our daily allowance
            double addition = Double.parseDouble(expense.getText().toString());
            double newValue = (Double.parseDouble(allowance.getText().toString())) + addition;
            allowance.setText(moneyFormat.format(newValue));
            expense.setText("");
        }catch(NumberFormatException e){
            Toast.makeText(this, "You need to enter a non-blank amount!", Toast.LENGTH_SHORT).show();
        }

        /** OLD CODE

        EditText expense = (EditText)findViewById(R.id.main_edittext_expenseentry);
        TextView allowance  = (TextView) findViewById(R.id.main_textview_allowance);

        expense.setText(moneyFormat.format(Double.parseDouble(expense.getText().toString())));
        expense.setTextColor(Color.GREEN);

        adapter.add(expense.getText().toString());
        adapter.notifyDataSetChanged();
        expense.setTextColor(Color.BLACK);

        // We need to add the new value from our daily allowance
        double addition = Double.parseDouble(expense.getText().toString());
        double newValue = (Double.parseDouble(allowance.getText().toString())) + addition;
        allowance.setText(moneyFormat.format(newValue));
        if(newValue < 0)
            allowance.setTextColor(Color.RED);
        else
            allowance.setTextColor(Color.BLACK);
        expense.setText("");
        */
    }

The LogCat:

03-30 15:51:36.831: E/AndroidRuntime(15713): FATAL EXCEPTION: main
03-30 15:51:36.831: E/AndroidRuntime(15713): java.lang.NumberFormatException: Invalid double: "1,000.00"
03-30 15:51:36.831: E/AndroidRuntime(15713):    at java.lang.StringToReal.invalidReal(StringToReal.java:63)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at java.lang.StringToReal.parseDouble(StringToReal.java:269)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at java.lang.Double.parseDouble(Double.java:295)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at com.argusrho.budgeteer.MainActivity$1.onClick(MainActivity.java:249)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:166)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at android.os.Handler.dispatchMessage(Handler.java:99)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at android.os.Looper.loop(Looper.java:137)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at android.app.ActivityThread.main(ActivityThread.java:5041)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at java.lang.reflect.Method.invokeNative(Native Method)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at java.lang.reflect.Method.invoke(Method.java:511)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
03-30 15:51:36.831: E/AndroidRuntime(15713):    at dalvik.system.NativeStart.main(Native Method)

Solution

  • The error occurs because ParseDouble doesn't know how to handle commas (,). I found the easiest way to fix this issue is to use ReplaceAll(",", "") on the String you want to parse beforehand.