0

I am modelling a system which contains a specific amount of energy between 0 and 1. This is stored in a double called storedEnergy.

storedEnergy should count from 0.0 to 1.0 over 21 minutes, but instead counts to 1.0000000000000004

Every iteration storedEnergy increases by onStoredEnergyIncreasePerMinute which is calculated like this:

  onSlackMinutesIncreasePerMinute = (double) time1to0Energy / (double) time0to1Energy;
  onStoredEnergyIncreasePerMinute = 1d / (double) time0to1Energy;
  offSlackMinutesDecreasePerMinute = 1d;
  offStoredEnergyDecreasePerMinute = 1d / (double) time1to0Energy;

Here is the stored energy increasing:

  while (storedEnergy < target) {
     storedEnergy += onStoredEnergyIncreasePerMinute;
  } 

A similar thing happens with slack minutes: there are 24.999999999999993 at 1.0 energy, when there should be 25 at 1.0

It may be relevant to mention that I will then count back down to 0 using offStoredEnergyDecreasePerMinute for 25 minutes

diagram 1

I don't know why this is happening (though I presume it's something to do with doubles not being able to represent fractions properly), or what I should do to resolve it. Do I have to use some sort of fraction class?

Odyssey
  • 89
  • 9
  • It has indeed to do with float, double and all floating point number representation... Since those numbers are saved as significant, base and exponent it is impossible to accurately display some numbers. http://en.wikipedia.org/wiki/Floating_point I would suggest using round fucntions like round() ceil() and floor() but since this is more a fix or workaround than a solution I won't post it as an answer.. – Barthy Feb 25 '15 at 14:59
  • Don't use a double for iteration unless you use rounding. Also don't use floating point, or decimals, unless you use rounding. – Peter Lawrey Feb 25 '15 at 15:02
  • Does Java have an unlimited-precision data type for rationals that you could use? Over in Python land, we have `Fraction`. Maybe this will help: [Best way to represent a fraction in Java?](http://stackoverflow.com/q/474535/953482) – Kevin Feb 25 '15 at 15:03
  • 1
    Floating-point numbers are not infinitely precise. See http://floating-point-gui.de/ – Jesper Feb 25 '15 at 15:08

3 Answers3

2

I presume it's something to do with doubles not being able to represent fractions properly

That's right, floating-point numbers can only represent binary fractions exactly, and up to a certain limit of precision.

You should carefully consider what set of numbers your computation uses. If it's just rational numbers, then you can relatively simply implement that with two integers. If you take square roots or similar, then no numerical representation will be exact.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
2

Looks like your over adding here:

 while (storedEnergy < target) {
     //this goes above 1.0 on the final iteration
     storedEnergy += onStoredEnergyIncreasePerMinute;  
  } 

you can fix by doing:

 while (storedEnergy < target) {
     storedEnergy += onStoredEnergyIncreasePerMinute;
     if(storedEnergy>target)
        storedEnergy = target;

  } 
Tucker
  • 7,017
  • 9
  • 37
  • 55
  • +1. This does assume that rounding off `storedEnergy` doesn't affect how the program runs, but if that's the case, then doubles aren't the right type to begin with. – Teepeemm Feb 25 '15 at 15:20
1

Read What Every Computer Scientist Should Know About Floating-Point Arithmetic to understand why these errors occur. Try setting the precision you want your variables to have or use rounding functions, to bypass the representation problem

igavriil
  • 1,001
  • 2
  • 12
  • 18