0

I have noticed a small error on some arithmetic calculations using double. It is really weird, there's always a small error and/or an extra significant digit.

First I am using atof to convert a number that has two significant digits that I am reading from a text file (then I record them on a vector):

 // Puts into vector
  double ask_file, bid_file; // Values of ask and bid from file
  double cur_conversion = 0.16;
  ask_file = cur_conversion*atof(values[0].c_str()); 
  bid_file = cur_conversion*atof(values[1].c_str()); 

Then I am doing the arithmetic (from other class, two different objects):

diff = OKC->bid_val() - BV->ask_val(); // diff
diff2 = OKC->ask_val() - BV->bid_val(); // diff2

This is the output:

BV Askfile: 245.267 Bidfile: 245.078 
OKC Askfile: 248.82 Bidfile: 248.73 
diff: 3.4628 diff2: 3.7416

As you can see, there's an error on both calculations. diff = 3.463 and NOT 3.4628. And diff2 = 3.742 and NOT 3.7416.

Do you know what's going on??

Luis Cruz
  • 1,488
  • 3
  • 22
  • 50
  • 5
    http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html is a good read – Ed Heal Feb 14 '15 at 19:50
  • 2
    How do you print these values? It doesn't look like you print them with maximum precision, so I'm guessing that the BV values are really (closer to) 245.2672 and 245.0784, which would be 0.16 * 1531.74 and 0.16 * 1532.92, respectively. I'm also guessing that these are absolute amounts of money, in which case you might want to reconsider doing these calculations with floating point numbers. – Wintermute Feb 14 '15 at 19:54
  • 1
    Do not use floating-point arithmetics in financial instruments. It is expected to carry and accumulate error due to value presentation form. – Valeri Atamaniouk Feb 14 '15 at 19:57
  • @ValeriAtamaniouk: actually, there is nothing wrong with using floating point arithmetic when dealing with financial instruments. However, the floating poins shall use base 10 rather than base 2 which is used for the built-in types `float`, `double`, or `long double`. – Dietmar Kühl Feb 14 '15 at 20:00
  • What do you guys recommend for financial calculations then? Is float better than double? Otherwise, what is the best way to do these calculations without any floating-point arithmetic? – Luis Cruz Feb 14 '15 at 20:04
  • 1
    @LuisCruz Use fixed point arithmetics, or arbitrary precision one - thus controlling all computation errors the way you need. – Valeri Atamaniouk Feb 14 '15 at 20:05
  • 1
    What *Valeri Atamaniouk* says is industry-standard. All floating point formats are almost always inappropriate for finance. You want to use something integer-like to represent *thousandths of a cent* or something similar. – Drew Dormann Feb 14 '15 at 20:16
  • What were the original inputs? Have you tried printing to higher precision to see what is going on? Errors in the fourth or fifth significant digit are not the standard rounding error issue, given a short series of double operations on numbers of about the same magnitude. – Patricia Shanahan Feb 15 '15 at 00:08
  • Do not using floating point numbers for currency. Use integers. People think if the account is out by a penny then what is happening to the pounds – Ed Heal Feb 15 '15 at 10:22

1 Answers1

1

The problem is that it is in general impossible to represent fractional decimal values exactly using binary floating point numbers. For example, 0.1 is represented as 1.000000000000000055511151231257827021181583404541015625E-1 when using double (you can use this online analyzer to determine the values). When computing with these rounded values the number of necessary binary digits will exceed those which can be represented and the value will be further rounded, introducing more error. Of course, all this is covered in Goldberg's paper pointed to by the comment of Ed Heal.

There are a number of alternative representation you can use to compute exactly with decimal values. Unless the representation uses an arbitrary sized representation it will be exactly only within some range of values. Typical choices are:

  1. Using a big integer representation together with a suitable decimal scaling.
  2. Strings (or BCDs) of digits.
  3. A fixed point representation which is basically just an integer together with a fixed decimal exponent where the exponent is implicit in the fixed point type (or, e.g., a template argument).
  4. Instead of using binary floating point you'd use decimal floating points. Floating points are just a representation of a sign, a significand, and an exponent with the value being computed as (-1)sign * significand * baseexponent. double uses a base of 2 but for decimal computations you'd use base 10.
  5. Using two big integers you could represent the value as a rational number.
  6. There are a couple of other choices but the above list is what I'd consider to be practical options.

Depending on the choice of implementation different operations are more or less easy to implement and the exact operations vary. For example, except for the representation using rational operations divisions will always be rounded when the divisor cannot be represented as a product of 2 and 5.

Which representation would work best for your application depends on your needs. If you have only trade prices in ranges typical for equities, a fixed point representation may work. If you need to cover all sorts of values you can encounter in finance, e.g., national debts as well as interest rates you'd need more than 64 bits for your fixed point representation and decimal floating point representation may be the better representation. Depending on whether you need to transfer and/or store the values a fixed size representation may not be required in which case the other representations may be a reasonable choice.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Are you sure this is the problem in this case? The error appears to be in about the fourth or fifth significant digit, after doing calculations with similar values. – Patricia Shanahan Feb 15 '15 at 00:43
  • @PatriciaShanahan: yes, I'm pretty sure that this is the problem although you won't get the error from the values printed directly. The represented values can actually be quite different while still printing as those above. For example, `double d0(248.7298)` would also print as `248.73` (assuming default settings) and yield the observed result (`3.4628`) when subtracting `245.267`. – Dietmar Kühl Feb 15 '15 at 05:22
  • What you describe in the comment is what I suspect, and the OP could fix it by simply printing more digits. The same issue could arise from rounding the output too aggressively even if the arithmetic were being done in real numbers. It is quite different from the inherent limitations of double precision that might require the more drastic fixes in your answer. – Patricia Shanahan Feb 15 '15 at 09:12