Java 8 BigDecimal乘法方法在被反转时丢失精度

Java 8 BigDecimal乘法方法在被反转时丢失精度,java,precision,bigdecimal,Java,Precision,Bigdecimal,这里是Java8。据谷歌称: 将磅换算为千克: 将千克换算为磅: 将英寸转换为米: 将米转换为英寸: 所以我编写了以下Java代码: public class MeasurementConverter { private static final int SCALE = 2; public static void main(String[] args) { new MeasurementConverter().run(); } private

这里是Java8。据谷歌称:

将磅换算为千克: 将千克换算为磅: 将英寸转换为米: 将米转换为英寸: 所以我编写了以下Java代码:

public class MeasurementConverter {
    private static final int SCALE = 2;

    public static void main(String[] args) {
        new MeasurementConverter().run();
    }

    private void run() {
        BigDecimal hundredLbs = new BigDecimal(100.00);
        BigDecimal hundredInches = new BigDecimal(100.00);

        System.out.println(hundredLbs + " pounds is " + poundsToKilos(hundredLbs) + " kilos and converts back to " + kilosToPounds(poundsToKilos(hundredLbs)) + " pounds.");
        System.out.println(hundredInches + " inches is " + inchesToMeters(hundredInches) + " meters and converts back to " + metersToInches(inchesToMeters(hundredInches)) + " inches.");
    }

    private BigDecimal metersToInches(BigDecimal meters) {
        return meters.multiply(new BigDecimal("39.3701").setScale(SCALE, BigDecimal.ROUND_HALF_UP));
    }

    private BigDecimal inchesToMeters(BigDecimal inches) {
        return inches.multiply(new BigDecimal("0.0254").setScale(SCALE, BigDecimal.ROUND_HALF_UP));
    }

    private BigDecimal kilosToPounds(BigDecimal kilos) {
        return kilos.multiply(new BigDecimal("2.20462").setScale(SCALE, BigDecimal.ROUND_HALF_UP));
    }

    private BigDecimal poundsToKilos(BigDecimal pounds) {
        return pounds.multiply(new BigDecimal("0.45359").setScale(SCALE, BigDecimal.ROUND_HALF_UP));
    }
}
当它运行时,会打印出:

100 pounds is 45.00 kilos and converts back to 99.0000 pounds.
100 inches is 3.00 meters and converts back to 118.1100 inches.
而我希望它能打印出来:

100.00 pounds is 45.00 kilos and converts back to 100.00 pounds.
100.00 inches is 3.00 meters and converts back to 100.00 inches.

我所关心的是ImperialMetric转换的精度高达小数点后2位,并且最终输出的精度始终高达小数点后2位。有人能看出我哪里出了差错,解决办法是什么吗?

乘法的比例和一半参数不是吗?您只能将其添加到BigDecimal

我想你想要:

inches.multiply(new BigDecimal("0.0254"), new MathContext(SCALE, RoundingMode.HALF_UP));
将比例更改为8,并将代码与MathContext一起使用,结果是:

100 pounds is 45.35900 kilos and converts back to 99.999359 pounds.
100 inches is 2.5400 meters and converts back to 100.00005 inches.

你的换算系数是关的。将转换因子四舍五入到小数点后两位:

private static final int SCALE = 2;
[...]    
new BigDecimal("0.0254").setScale(SCALE, BigDecimal.ROUND_HALF_UP)

因此,00254被四舍五入到0.03,这不符合39.37的反向转换系数。这就是为什么你的反转不等于原始值

你把括号弄乱了。考虑这一点:

kilos.multiply(new BigDecimal("2.20462").setScale(SCALE, BigDecimal.ROUND_HALF_UP));
在这里,您首先将比例设置为2.20462,结果为2.20,然后进行乘法

现在考虑反向转换:

pounds.multiply(new BigDecimal("0.45359").setScale(SCALE, BigDecimal.ROUND_HALF_UP));
这里你可以有效地乘以0.45。2.2*0.45=0.99,这解释了结果

您已根据乘法的结果而不是乘法器设置了比例。基本上,最后一个括号位于错误的位置。应该是这样的:

pounds.multiply(new BigDecimal("0.45359")).setScale(SCALE, BigDecimal.ROUND_HALF_UP);

这是.

你在转换中间做舍入。因此,反向操作的输入值已关闭

如果要将值显示为:

100.00 pounds is 45.00 kilos and converts back to 100.00 pounds.
100.00 inches is 3.00 meters and converts back to 100.00 inches.
从所有私有方法中删除setScale。例如:

private BigDecimal metersToInches(BigDecimal meters) {
    return meters.multiply(new BigDecimal("39.3701"));
}
然后,您只需使用所需格式格式化输出:

private BigDecimal formatOutput(BigDecimal value) {
    return value.setScale(0, BigDecimal.ROUND_HALF_UP).setScale(SCALE);
}
最后:

System.out.println(hundredLbs + " pounds is " + formatOutput(poundsToKilos(hundredLbs)) + " kilos and converts back to " + formatOutput(kilosToPounds(poundsToKilos(hundredLbs))) + " pounds.");
System.out.println(hundredInches + " inches is " + formatOutput(inchesToMeters(hundredInches)) + " meters and converts back to " + formatOutput(metersToInches(inchesToMeters(hundredInches))) + " inches.");
给出:

100.00 pounds is 45.00 kilos and converts back to 100.00 pounds.
100.00 inches is 3.00 meters and converts back to 100.00 inches.
或者,如果要显示带小数点的值,只需将formatOutput方法更改为:

private BigDecimal formatOutput(BigDecimal value) {
    return value.setScale(SCALE, BigDecimal.ROUND_HALF_UP);
}
这将以下面的方式打印出值,我认为这是更好和正确的

100.00 pounds is 45.36 kilos and converts back to 100.00 pounds.
100.00 inches is 2.54 meters and converts back to 100.00 inches.

请注意,100磅等于45.36千克,100英寸等于2.54米。请记住,将双精度传递给新的BigDecimal会失去精度,因为您使用的不是精确值,而是最接近的可表示值作为双精度。改用新的大小数39.37等。还请记住,这些相互作用并不精确,因此39.37*0.0254=0.99998,而不是1。谢谢@AndyTurner+1。不过,请查看我对您建议的更改的更新。转换仍然遥不可及!有什么想法吗?我之前的评论只是一个暗示。增加刻度。但为什么还要麻烦设置刻度呢?@OliverCharlesworth这是一个问题,就像OP一样。事实上——我的建议是,答案应该是删除设置刻度;然后将其放入输出格式谢谢@Marcos+1-新的MathContext实例有多贵/多重?有没有办法在应用程序启动时只创建一次?我这样问是因为在野外,这些方法会一直受到攻击……我认为MathContext只会在计算后应用