1

I am trying to make a basic calculator program for android but I am new to android as well as java
But I believe this is probably more of a java problem
The interface has buttons for 0 through 9, decimal point, equal to sign, +, -, *, / and Cancel (which resets all the variables)
The mantissa is stored in a double variable and I use String.valueOf(mantissa_value) to convert it to a String to be displayed on a TextView control
When I type in 2.331, the mantissa shows 2.33099999 (10 characters is the limit for the String variable storing the mantissa text), but if I press 1 again, it shows 2.3311. Why is this happening and how can I fix it?

Relevant code (probably):

public void onClkBn1(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+1;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*1;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void updateMantissa(){
    if(mantissa_value == 0){
        mantissa_str = "0";
    }

    else if(ceil(mantissa_value) == mantissa_value){
        mantissa_str = String.valueOf(mantissa_value);
        mantissa_str = mantissa_str.substring(0, mantissa_str.length() - 2 ); 
    }

    else{
        mantissa_str = String.valueOf(mantissa_value);
        mantissa_str = mantissa_str.substring(0, Math.min(mantissa_str.length(), 10));
    }
    TextView thisText = (TextView) findViewById(R.id.mantissa);
    thisText.setText(mantissa_str);
}

Full code:

package com.example.calculator;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
import static java.lang.Math.pow;
import static java.lang.Math.ceil;

public class MainActivity extends Activity {
private double mantissa_value = 0;
private double opr1 = 0;
private double opr2 = 0;
private double res = 0;
private enum OprTypes {NONE, PLUS, MINUS, MULT, DIV}
OprTypes oprLatest = OprTypes.NONE;
private boolean oprPlusClicked = false;
private boolean oprMinusClicked = false;
private boolean oprMultClicked = false;
private boolean oprDivClicked = false;
private boolean decimalClicked = false;
private double numOfClicksAfterDecimal = 1;
private String mantissa_str;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    updateMantissa();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

public void updateMantissa(){
    if(mantissa_value == 0){
        mantissa_str = "0";
    }

    else if(ceil(mantissa_value) == mantissa_value){
        mantissa_str = String.valueOf(mantissa_value);
        mantissa_str = mantissa_str.substring(0, mantissa_str.length() - 2 ); 
    }

    else{
        mantissa_str = String.valueOf(mantissa_value);
        mantissa_str = mantissa_str.substring(0, Math.min(mantissa_str.length(), 10));
    }
    TextView thisText = (TextView) findViewById(R.id.mantissa);
    thisText.setText(mantissa_str);
}

public void onClkBn0(View view){
    TextView thisText = (TextView) findViewById(R.id.mantissa);
    if(decimalClicked == false){
        mantissa_value = mantissa_value*10+0;
        updateMantissa();
    }
    else{
        if(mantissa_value == 0){
            if(numOfClicksAfterDecimal == 1){
                mantissa_str = mantissa_str + ".0";
            }
            else{
                mantissa_str = mantissa_str + "0";
            }
        }

        else if(ceil(mantissa_value) == mantissa_value){
            if(numOfClicksAfterDecimal == 1){
                mantissa_str = mantissa_str + ".0";
            }
            else{
                mantissa_str = mantissa_str + "0";
            }
        }

        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*0;
        numOfClicksAfterDecimal++;
        thisText.setText(mantissa_str);
    }
}

public void onClkBn1(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+1;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*1;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn2(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+2;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*2;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn3(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+3;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*3;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn4(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+4;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*4;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn5(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+5;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*5;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn6(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+6;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*6;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn7(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+7;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*7;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn8(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+8;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*8;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBn9(View view){
    if(decimalClicked == false)
        mantissa_value = mantissa_value*10+9;
    else{
        mantissa_value = mantissa_value + pow(10, -numOfClicksAfterDecimal)*9;
        numOfClicksAfterDecimal++;
    }
    updateMantissa();
}

public void onClkBnDot(View view){
    decimalClicked = true;
}

public void onClkBnCan(View view){
    mantissa_value = 0;
    opr1 = 0;
    opr2 = 0;
    res = 0;
    oprLatest = OprTypes.NONE;
    oprPlusClicked = false;
    oprMinusClicked = false;
    oprMultClicked = false;
    oprDivClicked = false;
    decimalClicked = false;
    numOfClicksAfterDecimal = 1;
}


public void onClkBnPlus(View view){
    if(oprPlusClicked == false){
        oprPlusClicked = true;
        opr1 = mantissa_value;
        mantissa_value = 0;
    }
    else{
        // this should mean opr1 already has some value in it
        // so add the current mantissa value to opr1
        opr1 = opr1 + mantissa_value;
        mantissa_value = 0;
    }
    oprLatest = OprTypes.PLUS;
}


public void onClkBnMinus(View view){
    if(oprMinusClicked == false){
        oprMinusClicked = true;
        opr1 = mantissa_value;
        mantissa_value = 0;
    }
    else{
        // this should mean opr1 already has some value in it
        // so add the current mantissa value to opr1
        opr1 = opr1 - mantissa_value;
        mantissa_value = 0;
    }
    oprLatest = OprTypes.MINUS;
}

public void onClkBnMult(View view){
    if(oprMultClicked == false){
        oprMultClicked = true;
        opr1 = mantissa_value;
        mantissa_value = 0;
    }
    else{
        // this should mean opr1 already has some value in it
        // so add the current mantissa value to opr1
        opr1 = opr1 * mantissa_value;
        mantissa_value = 0;
    }
    oprLatest = OprTypes.MULT;
}

public void onClkBnDiv(View view){
    if(oprDivClicked == false){
        oprDivClicked = true;
        opr1 = mantissa_value;
        mantissa_value = 0;
    }
    else{
        // this should mean opr1 already has some value in it
        // so add the current mantissa value to opr1
        opr1 = opr1 / mantissa_value;
        mantissa_value = 0;
    }
    oprLatest = OprTypes.DIV;
}


public void onClkBnRes(View view){
    if(oprPlusClicked == true || oprMinusClicked == true || oprMultClicked == true || oprDivClicked == true){
        switch(oprLatest){
        case PLUS:
            mantissa_value = opr1 + mantissa_value;
            break;
        case MINUS:
            mantissa_value = opr1 - mantissa_value;
            break;
        case MULT:
            mantissa_value = opr1 * mantissa_value;
            break;
        case DIV:
            mantissa_value = opr1 / mantissa_value;
            break;
        case NONE:
            break;
        }
    }
    oprLatest = OprTypes.NONE;
    oprPlusClicked = false;
    oprMinusClicked = false;
    oprMultClicked = false;
    oprDivClicked = false;
    updateMantissa();
}
}
user207421
  • 305,947
  • 44
  • 307
  • 483
user13267
  • 6,871
  • 28
  • 80
  • 138
  • @JBNizet: So this is not a java issue then? What if I was just using the double variables in my program instead of trying to display them as text? This should not have any significant effect in that case? – user13267 Jul 12 '13 at 08:34
  • No, it's not a Java issue. It's a finite vs. infinite issue. You can't represent an infinity or real values using 64 bits. If you want exact values, don't use double. If approximate values are fine, then double is OK. – JB Nizet Jul 12 '13 at 08:40
  • So 2.331 does not have an exact representation, but 2.3311 does? To fix this, how would I check which particular floating point number does not have an exact representation, or is it possible to get String.valueOf() to get the string equivalent of the approximate value (using just the basic data types and not the BigDecimal as suggested in the answer)? – user13267 Jul 12 '13 at 08:48
  • String.valueOf() returns the string representation of the double value that is the closest to the one you want: 2.33099999 is the closest to 2.331 – JB Nizet Jul 12 '13 at 09:15

1 Answers1

1

A Double in Java is not exact. It can be fixed by using BigDecimal

See How to resolve a Java Rounding Double issue

Community
  • 1
  • 1
Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
  • Why is it like that in Java? What if I was just using the double variable in a long series of calculations, instead of trying to display it as text? Would this cause any significant change in the final result in that case, or would it be negligible for most calculations? – user13267 Jul 12 '13 at 08:36
  • IMO wrong is wrong. I have seen so many people caught out especially when using Double for money. You can be your own judge as to whether you can 'live' with using a double, but maybe years later you will regret it. – Scary Wombat Jul 12 '13 at 08:47