The following code has a precision error but I'm at a loss as to how to resolve it. Given an initial value set in dollar/gal of $3.95, when change the ChoiceBox to "dollars/liter" I get $1.04, but when I change it back to dollar/gal I'm getting $3.94 instead of $3.95
if ("dollar/gal".equals(newValue)) {
double pricePerLiter = Double.parseDouble(txtFields.get(1).getText());
BigDecimal pricePerGallon = BigDecimal.valueOf(pricePerLiter * 3.785);
pricePerGallon = pricePerGallon.setScale(2, RoundingMode.HALF_UP);
txtFields.get(1).setText(pricePerGallon.toString());
} else if ("dollars/liter".equals(newValue)) {
double pricePerGallon = Double.parseDouble(txtFields.get(1).getText());
BigDecimal pricePerLiter = BigDecimal.valueOf(pricePerGallon * 0.264);
pricePerLiter = pricePerLiter.setScale(2, RoundingMode.HALF_UP);
txtFields.get(1).setText(pricePerLiter.toString());
}
I've attempted this with both Math.Round() and BigDecimal and RoundingMode (which is the version above) but cannot resolve this issue.
This is not a floating-point representation issue; it's a rounding issue. When you round, your value is no longer accurate. In addition, the two values you multiply with don't themselves multiply to be 1; they're not quite inverses.
When you take
3.95and multiply it by0.264, the value is1.0428, which you round down to1.04. When you multiply1.04by3.785, you get3.9364, which is rounded up to3.94.But you represent the inverse of
3.785as0.264, which is close, but not quite equal to the actual inverse of about0.2642008. But even multiplying the true inverse value by3.95you get1.04359316, which you would still round down to1.04.If you must convert back to the same value, then store the un-rounded value for multiplying later, even if you display the rounded value. Also, instead of multiplying by
0.264, just divide by the same number that you multiplied by in the other case,3.785.