使用BigDecimal会导致计算中出现轻微错误

时间:2014-10-24 12:17:27

标签: android biginteger

我正在为android编写一个基本的单位转换器。这是我的计划:

package com.airavataunitconverter;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;

import com.airavataunitconverter.R.id;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

public class LengthConverter extends Activity {
  Spinner spr_unit_l;
  EditText et_input_l;
  TextView tv_result_nm;
  TextView tv_result_mm;
  TextView tv_result_cm;
  TextView tv_result_dm;
  TextView tv_result_m;
  TextView tv_result_km;
  TextView tv_result_in;
  TextView tv_result_ft;
  TextView tv_result_yd;
  TextView tv_result_mi;
  TextView tv_result_nmi;
  TextView tv_nm;
  TextView tv_mm;
  TextView tv_cm;
  TextView tv_dm;
  TextView tv_m;
  TextView tv_km;
  TextView tv_in;
  TextView tv_ft;
  TextView tv_yd;
  TextView tv_mi;
  TextView tv_nmi;
  BigDecimal nm;
  BigDecimal mm;
  BigDecimal cm;
  BigDecimal dm;
  BigDecimal m;
  BigDecimal km;
  BigDecimal in ;
  BigDecimal ft;
  BigDecimal yd;
  BigDecimal mi;
  BigDecimal nmi;
  BigDecimal zero;
  BigDecimal bd_input_number_length;
  String str_nm = "nanometer";
  String str_mm = "millimeter";
  String str_cm = "centimeter";
  String str_dm = "decimeter";
  String str_m = "meter";
  String str_km = "kilometer";
  String str_in = "inch";
  String str_ft = "feet";
  String str_yd = "yard";
  String str_mi = "mile";
  String str_nmi = "nautical mile";
  String STR_INPUT_UNIT_LENGTH;
  int input_unit_length;
  int int_input_unit_length;
  String STR_INPUT_NUMBER_LENGTH;
  String str_unit_length;
  ArrayAdapter < String > myAdap;
  int unit_selected_nm;
  int unit_selected_mm;
  int unit_selected_cm;
  int unit_selected_dm;
  int unit_selected_m;
  int unit_selected_km;
  int unit_selected_in;
  int unit_selected_ft;
  int unit_selected_yd;
  int unit_selected_mi;
  int unit_selected_nmi;
  String input_number_length;
  BigDecimal bd_referance;
  BigDecimal bd_nm;
  BigDecimal bd_mm;
  BigDecimal bd_cm;
  BigDecimal bd_dm;
  BigDecimal bd_m;
  BigDecimal bd_km;
  BigDecimal bd_in;
  BigDecimal bd_ft;
  BigDecimal bd_yd;
  BigDecimal bd_mi;
  BigDecimal bd_nmi;
  @
  Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_length_converter);
    spr_unit_l = (Spinner) findViewById(R.id.spinner_length_unit);
    nm = new BigDecimal(0.000000001);
    mm = new BigDecimal(.001);
    cm = new BigDecimal(0.01);
    dm = new BigDecimal(0.1);
    m = new BigDecimal(1);
    km = new BigDecimal(1000); in = new BigDecimal(0.0254);
    ft = new BigDecimal(0.3048);
    yd = new BigDecimal(0.9144);
    mi = new BigDecimal(1609.344);
    nmi = new BigDecimal(1852);
    zero = BigDecimal.ZERO;
    String str_unit_length[] = {
      str_nm, str_mm, str_cm, str_dm, str_m,
      str_km, str_in, str_ft, str_yd, str_mi, str_nmi
    };
    myAdap = new ArrayAdapter < String > (this,
      android.R.layout.simple_spinner_item, str_unit_length);
    myAdap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spr_unit_l.setAdapter(myAdap);
    sharedpref = getSharedPreferences(MyPreferences, Context.MODE_PRIVATE);
    et_input_l = (EditText) findViewById(R.id.edittext_length_input);
    tv_nm = (TextView) findViewById(R.id.textview_nm);
    tv_mm = (TextView) findViewById(R.id.textview_mm);
    tv_cm = (TextView) findViewById(R.id.textview_cm);
    tv_dm = (TextView) findViewById(R.id.textview_dm);
    tv_m = (TextView) findViewById(R.id.textview_m);
    tv_km = (TextView) findViewById(R.id.textview_km);
    tv_in = (TextView) findViewById(R.id.textview_in);
    tv_ft = (TextView) findViewById(R.id.textview_ft);
    tv_yd = (TextView) findViewById(R.id.textview_yd);
    tv_mi = (TextView) findViewById(R.id.textview_mi);
    tv_nmi = (TextView) findViewById(R.id.textview_nmi);
    tv_result_nm = (TextView) findViewById(R.id.textview_result_nm);
    tv_result_mm = (TextView) findViewById(R.id.textview_result_mm);
    tv_result_cm = (TextView) findViewById(R.id.textview_result_cm);
    tv_result_dm = (TextView) findViewById(R.id.textview_result_dm);
    tv_result_m = (TextView) findViewById(R.id.textview_result_m);
    tv_result_km = (TextView) findViewById(R.id.textview_result_km);
    tv_result_in = (TextView) findViewById(R.id.textview_result_in);
    tv_result_ft = (TextView) findViewById(R.id.textview_result_ft);
    tv_result_yd = (TextView) findViewById(R.id.textview_result_yd);
    tv_result_mi = (TextView) findViewById(R.id.textview_result_mi);
    tv_result_nmi = (TextView) findViewById(R.id.textview_result_nmi);
    spr_unit_l.setOnItemSelectedListener(new OnItemSelectedListener() {

      @
      Override
      public void onItemSelected(AdapterView <? > arg0, View arg1,
        int arg2, long arg3) {
        // TODO Auto-generated method stub
        final_length_convert();

      }

      @
      Override
      public void onNothingSelected(AdapterView <? > arg0) {
        // TODO Auto-generated method stub

      }

    });
    et_input_l.addTextChangedListener(new TextWatcher() {

      @
      Override
      public void onTextChanged(CharSequence arg0, int arg1, int arg2,
        int arg3) {
        // TODO Auto-generated method stub
        final_length_convert();

      }

      @
      Override
      public void beforeTextChanged(CharSequence arg0, int arg1,
        int arg2, int arg3) {
        // TODO Auto-generated method stub

      }

      @
      Override
      public void afterTextChanged(Editable arg0) {
        // TODO Auto-generated method stub

      }
    });

  }

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

  private int getIndex(Spinner spinner, String myString) {

    int index = 0;

    for (int i = 0; i < spinner.getCount(); i++) {
      if (spinner.getItemAtPosition(i).equals(myString)) {
        index = i;
        i = spr_unit_l.getCount();
      }
    }
    return index;
  }

  private void final_length_convert() {
    if (et_input_l.getText().toString().equals("") || et_input_l.getText().toString().equals(".") || et_input_l.getText().toString().equals("-")) {
      tv_result_nm.setText(zero.toPlainString());
      tv_result_mm.setText(zero.toPlainString());
      tv_result_cm.setText(zero.toPlainString());
      tv_result_dm.setText(zero.toPlainString());
      tv_result_m.setText(zero.toPlainString());
      tv_result_km.setText(zero.toPlainString());
      tv_result_in.setText(zero.toPlainString());
      tv_result_ft.setText(zero.toPlainString());
      tv_result_yd.setText(zero.toPlainString());
      tv_result_mi.setText(zero.toPlainString());
      tv_result_nmi.setText(zero.toPlainString());
    } else if (spr_unit_l.getSelectedItem().equals(str_nm)) {
      bd_referance = nm;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_mm)) {
      bd_referance = mm;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_cm)) {
      bd_referance = cm;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_dm)) {
      bd_referance = dm;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_m)) {
      bd_referance = m;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_km)) {
      bd_referance = km;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_in)) {
      bd_referance = in ;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_ft)) {
      bd_referance = ft;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_yd)) {
      bd_referance = yd;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_mi)) {
      bd_referance = mi;
      length_convert();
    } else if (spr_unit_l.getSelectedItem().equals(str_nmi)) {
      bd_referance = nmi;
      length_convert();
    }

  }

  private void length_convert() {
    bd_input_number_length = new BigDecimal(et_input_l.getText().toString());
    bd_nm = bd_input_number_length.multiply(
        (bd_referance).divide(nm, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_mm = bd_input_number_length.multiply(
        (bd_referance).divide(mm, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_cm = bd_input_number_length.multiply(
        (bd_referance).divide(cm, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_dm = bd_input_number_length.multiply(
        (bd_referance).divide(dm, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_m = bd_input_number_length.multiply(
        (bd_referance).divide(m, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_km = bd_input_number_length.multiply(
        (bd_referance).divide(km, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_in = bd_input_number_length.multiply(
        (bd_referance).divide( in , 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_ft = bd_input_number_length.multiply(
        (bd_referance).divide(ft, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_yd = bd_input_number_length.multiply(
        (bd_referance).divide(yd, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_mi = bd_input_number_length.multiply(
        (bd_referance).divide(mi, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    bd_nmi = bd_input_number_length.multiply(
        (bd_referance).divide(nmi, 4, RoundingMode.HALF_EVEN))
      .stripTrailingZeros();
    tv_result_nm.setText(bd_nm.toPlainString());
    tv_result_mm.setText(bd_mm.toPlainString());
    tv_result_cm.setText(bd_cm.toPlainString());
    tv_result_dm.setText(bd_dm.toPlainString());
    tv_result_m.setText(bd_m.toPlainString());
    tv_result_km.setText(bd_km.toPlainString());
    tv_result_in.setText(bd_in.toPlainString());
    tv_result_ft.setText(bd_ft.toPlainString());
    tv_result_yd.setText(bd_yd.toPlainString());
    tv_result_mi.setText(bd_mi.toPlainString());
    tv_result_nmi.setText(bd_nmi.toPlainString());
  }

  public void save(View view) {
    sharedpref_editor = sharedpref.edit();
    sharedpref_editor.putInt(STR_INPUT_UNIT_LENGTH,
      spr_unit_l.getSelectedItemPosition());
    sharedpref_editor.commit();
  }
}

我已经写过它,因此当用户键入或更改微调器中的项目时,不会自动为用户进行转换,即不需要转换&#34;按钮。我最初使用double计算数字,但有时会导致错误,如0.9999999999997,当它应该显示1.在搜索解决方案后,我认为最好使用BigDecimal,因为他们没有这些类型的错误。不幸的是,这些类型的错误仍然存​​在(例如,输入km = 1显示nm = 999999999999.9999而不是1000000000000,但其他转换仍然正确)。谁能指出我做错了什么?

1 个答案:

答案 0 :(得分:1)

正如我在评论中提到的,您的问题是在BigDecimal构造函数中输入双精度,这是您在键入new BigDecimal(0.000000001);时所执行的操作。从概念上讲,您正在执行以下操作

  1. 基础10表示类型
  2. 转换为Base 2 double表示(引入非常小的错误)
  3. 转换为Base 10 BigDecimal表示。
  4. 正如您所看到的,您无法避免Base 10和Base 2之间转换的初始错误。

    您需要做的是使用String BigDecimal输入来避免中间Base 2表示:

    new BigDecimal("0.000000001");