Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Floating Point Precision Issue

I have a question regarding floating point arithmetic in java and its precision. I did do my research on here and via google and come across some solutions but am having difficulties implementing them in my design. So in Java I am making use of the BigDecimal class in getting my calculations accurate. Note that the variables are double and the values can have a precision up to 8 decimal places to the right when doing calculations. The result (precision) to display is known and thats what I'll be storing as the current value. Also, all values come in dynamically (through a method). The argument passed should be the currentValue + the step size.

public void newValue(float value) {

  //Clip to valid range, can't go over minimum/max value
  value = Math.max(minimumValue, Math.min(maximumValue, value));

  // TODO Implement better Floating Point Arithmetic precision

  MathContext mcI = new MathContext(0, RoundingMode.HALF_UP);      
  MathContext mcF = new MathContext(8, RoundingMode.HALF_UP);
  BigDecimal valueBD = new BigDecimal(value, mcF);
  BigDecimal minimumBD = new BigDecimal(minimumValue, mcF);
  BigDecimal stepBD = new BigDecimal(step, mcF);
  BigDecimal currentValueBD = new BigDecimal(currentValue, mcF);


  BigDecimal totalStepsBD = valueBD.subtract(minimumBD, mcF);

  //Ensure value is divisible by stepsize
  totalStepsBD = totalStepsBD.divide(stepBD, mcI);

  valueBD = stepBD.multiply(totalStepsBD, mcF);

  valueBD = valueBD.add(minimumBD, mcF);

  // arithmetic without using BigDecimal (old)
  //int totalSteps = (int) ((value- minimumValue)/ step);
  //value = totalSteps * step + minimumValue;

  if(!(valueBD.equals(currentValueBD))) {
     valueBD = valueBD.setScale(displayPrecision, RoundingMode.HALF_UP);
     currentValue = valueBD.floatValue();
     dispatch();
  }

}

Now, it works with some values but not all. Especially when I mess with the step size. So if step = 0.1 it was fine. If I made it 0.005, I'd get an AirthmeticException - non terminating decimal expansion on the step where

totalStepsBD = totalStepsBD.divide(stepBD, mcI);

When .005 is set for the step variable, after making a BigDeciaml (stepBD) it comes out to .0049999999... Not sure if that helps but if you have any ideas please let me know. Thank you.

like image 242
New Guy Avatar asked Dec 19 '14 14:12

New Guy


1 Answers

Pass a String step (and a String value) to the BigDecimal constructor. You can't precisely represent 0.005 as a double (or a float).

 BigDecimal stepBD = new BigDecimal("0.005"); // <-- works as a `String`.

Edit

Or as noted below, use BigDecimal.valueOf(double)

 BigDecimal stepBD = BigDecimal.valueOf(0.005);
like image 162
Elliott Frisch Avatar answered Nov 17 '22 23:11

Elliott Frisch