real*8 resolution not correct

To start, I attempted to search for an answer to this but am not quite sure what to search for.

I have a test program:

      program realtest

      real*8 V1, V2, V3
      integer*4 i

      V1 = 1.0d0
      V2 = 0.1d0
      V3 = 0.0d0

      do i = 1,50
        V3 = V1+V2
        write(*,77) V3
 77     FORMAT(F35.30)
        V1 = V3


V1 and V2 have the proper values, with 0’s filled in. However after the 6th iteration my V3 = 1.600000000000001 and the resolution continues to fluctuate for the values after that. Is there a way to specifiy the ‘d0’ in the V3=V2+V1 statement or am I doing something wrong like in my other posting on real*8’s? Thanks for helping me understand this better.

Hi bland,

What’s happening here is not a Fortran specific issue. Rather, most binary real numbers can not represent exactly their decimal equivlent. Think about what the decimal equivlent of the fraction 1/3 is, .333333333… The fraction 1/3 can not be represented exactly as a decimal so in every day life we always use an approximate value.

With the binary representation of real values, the decimal “0.1” can not be represented exactly. Instead, an approximation “0.10000000000000001” is used instead (the exact approximation will vary by system). As you add more and more values together, the error introduced by the approximation will grow.

There is a lot on the web about this issue, so doing a web search for “binary real number representation” might help.

  • Mat

I understand what you wrote, but I don’t understand how adding 1.0d0 + 0.1d0 can some how yeild 1.100000000000000100000000000. This crashes any loop we try to create that is dependent on the output as shown in my test case. We are trying to see if something is equal to 1.5d0 (for example) but the output never equals that exactly.

Is there a way to fill in the rest of the output values with zeros as we do with the d0 on the constants? I definitely see these resolution differences between Linux, Tru64, and VMS all of which have different “epsilons.” How would you work around something like this?

Thanks for the help.

Hi Bland,

Since 0.1 has an infinite binary expansion, even in double precision (ie 0.1d0) it can not be represented as an exact binary number. Adding the extra " .0000000000000001" is the closest your hardware can come to it. Other hardware might get closer, but will never be exact.

The best way to think about a REAL value is that it is an approximation and thus always wrong. It just a matter of how wrong. Granted some values can be represented, but think of these as the exception not the rule. As such, a REAL should not be used to detemine if something is equal.

One way around this is to account for the error and test if the sum is within a specified delta. Don’t test for an exact match, just check if the value is within a range.

Another option if you are only using larger fractional amounts, such as .1, is to scale your value and use a INTEGER instead. An INTEGER can be used to check for equilvance. For example, if your summing U.S. dollars, instead of summing the dollars and cents, simply sum the cents. In other words, scale everything by 100 and use intergers in place of your reals. If you need more precision, simply scale by a larger amount.

Hope this helps,