TransWikia.com

BigDecimalを用いても四捨五入されません。

スタック・オーバーフロー Asked by yuki yuki on September 1, 2021

以下のコードをAndroid Studioで一応実行することはできるのですが、calorie に対して BigDecimal で setScale をしているにもかかわらず、四捨五入されずに小数点以下5桁くらいまで表示されてしまっています。

calorie を求めるにあたって weight, height, age は BigDecimal での計算はしていません。最終的に求まる calorie に対して BigDecimal で setScale をしたら四捨五入されるという認識でいました。

import android.os.Bundle;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.math.*;


public class MainActivity extends AppCompatActivity {


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

    }

    // 決定ボタン
    public void onClickButton(android.view.View view) {
        TextView calorieForm = this.findViewById(R.id.calorieForm);
        TextView proteinForm = this.findViewById(R.id.proteinForm);
        TextView carbonForm = this.findViewById(R.id.carbonForm);
        TextView fatForm = this.findViewById(R.id.fatForm);
        EditText ageForm = this.findViewById(R.id.ageForm);
        EditText weightForm = this.findViewById(R.id.weightForm);
        EditText heightForm = this.findViewById(R.id.heightForm);

        // Spinnerオブジェクトを取得
        Spinner spinner = findViewById(R.id.spinnerSex);
        Spinner spinner1 = findViewById(R.id.活動レベルForm);

        // 選択されているアイテムを取得
        String item = (String)spinner.getSelectedItem();
        String item1 = (String)spinner1.getSelectedItem();


        String strAge;
        strAge = ageForm.getText().toString();
        String strWeight;
        strWeight = weightForm.getText().toString();
        String strHeight;
        strHeight = heightForm.getText().toString();

        double protein, carbon, fat, age, weight, height;
        age = Double.parseDouble(strAge);
        weight = Double.parseDouble(strWeight);
        height = Double.parseDouble(strHeight);

        // case文で処理したい
        double calorie = 0;
        BigDecimal bd = BigDecimal.valueOf(calorie);
        bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);      /* 左辺のbdが黒文字化?反映されていない */


        if (item.equals("男性") && item1.equals("ほぼ運動しない")) {
            calorie = (13.397 * weight + 4.799 * height - 5.677 * age + 88.362) * 1.2;
        } else if (item.equals("男性") && item1.equals("軽い運動をしている")) {
            calorie = (13.397 * weight + 4.799 * height - 5.677 * age + 88.362) * 1.375;
        } else if (item.equals("男性") && item1.equals("中程度の運動をしている")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.55;
        } else if (item.equals("男性") && item1.equals("激しい運動をしている")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.725;
        }  else if (item.equals("男性") && item1.equals("非常に激しい運動をしている")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.9;
        }  else if (item.equals("女性") && item1.equals("ほぼ運動しない")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.2;
        }  else if (item.equals("女性") && item1.equals("軽い運動をしている")) {
            calorie = (13.397 * weight + 4.799 * height - 5.677 * age + 88.362) * 1.375;
        } else if (item.equals("女性") && item1.equals("中程度の運動をしている")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.55;
        } else if (item.equals("女性") && item1.equals("激しい運動をしている")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.725;
        }  else if (item.equals("女性") && item1.equals("非常に激しい運動をしている")) {
            calorie = (9.247 * weight + 3.098 * height - 4.33 * age + 447.593) * 1.9;
        }
        protein = weight * 2.3;
        carbon = weight * 2.65;
        fat = weight * 0.9;


        String msg0 = calorie + "cal";
        String msg1 = protein + "g";
        String msg2 = carbon + "g";
        String msg3 = fat + "g";

        calorieForm.setText(msg0);
        proteinForm.setText(msg1);
        carbonForm.setText(msg2);
        fatForm.setText(msg3);
    }

3 Answers

こんにちは、はじめまして。

BigDecimalは大きな数値を精度をコントロールして利用するためのクラスになり、通常の数値型に代替するものではありません。

https://docs.oracle.com/javase/jp/8/docs/api/java/math/BigDecimal.html
https://techacademy.jp/magazine/18597

浮動小数点の精度により丸め誤差が生じるため存在します。僕の分野ではあまり使うことはないですが、精度を予測内に収めたい場合などには便利そうですね。

上のtechacademyからの引用ですが下記のように扱います。

BigDecimal b1 = new BigDecimal("3.1415");
BigDecimal b2 = new BigDecimal("9.9");
BigDecimal b3 = b1.multiply(b2);

すべての値をBigDecimalにして上記のようにメソッドで演算すれば目的の結果が得られるはずです。

ですがコードの目的は小数点1桁で四捨五入をして結果を表示したいという点ではないでしょうか?
weight,heightなどがdoubleと仮定して下記の式を小数点1桁で丸めるとすると下記の二つの例が考えられます。

calorie1. 10倍したものを四捨五入して、割りなおす。四捨五入にはMath.round命令を用います。
calorie2. 出力時に文字列にするのでその時点でString.formatする(仕様上、自動的に四捨五入されます)。

import java.lang.Math;

public class MyClass {
    public static void main(String args[]) {
      double weight = 65.23;
      double height = 171.15;
      double age = 28.45234;

      double calorie1 = (Math.round(((13.397 * weight + 4.799 * height - 5.677 * age + 88.362) * 1.2)*10))/10.0;
      System.out.println("calorie=" + calorie1);
      double calorie2 = (13.397 * weight + 4.799 * height - 5.677 * age + 88.362) * 1.2;
      System.out.println("calorie=" + String.format("%.1f",calorie2));
    }
}

参考 URL :
https://blog.apar.jp/program/8900/
http://hensa40.cutegirl.jp/archives/5620

Correct answer by Kouki.W on September 1, 2021

calorie に対して BigDecimal で setScale をしているにもかかわらず、四捨五入されずに小数点以下5桁くらいまで表示されてしまっています。

質問されている事項の原因は、出力対象にしている caloriedouble 型であり、 BigDecimal 型として宣言している bd は計算過程に一切関与していないためです。

// 表示対象として利用しているのは bd(BigDecimal) でなく calorie(double)
String msg0 = calorie + "cal";

代わりに、ここで calorie の値を BigDecimal に変換して利用します。

BigDecimal bd = new BigDecimal(calorie);
bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
// 表示対象は calorie の値を BigDecimal型に変換したもの
String msg0 = bd.toString() + "cal";

ちなみに今回のように出力を整形したいだけの場合は、(BigDecimal に変換するのではなく) double型変数の値に対して String.format()書式文字列fを適用するのが自然かなと思います。

String msg0 = String.format("%.1fcal", calorie);

Answered by DEWA Kazuyuki - 出羽和之 on September 1, 2021

回答していただきありがとうございます。

Math.round を使ってみた結果、少数1桁で表示させることができました。
System.out.println が上手く噛み合わなかったので、その部分は使いませんでした。原因として、最後のほうに

String msg0 = calorie + "cal"; 
calorieForm.setText(msg0);

を用いてtexitviewに反映されるようにしてあるかなと考えています。全然違っていたらすみません。

double calorie = 0;
if (item.equals("男性") && item1.equals("ほぼ運動しない")) {
    calorie = (Math.round((13.397 * weight + 4.799 * height - 5.677 * age + 88.362) * 1.2)*10)/10.0;
} 

String msg0 = calorie + "cal"; 
calorieForm.setText(msg0);

Answered by yuki yuki on September 1, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP