6

I'm literately just doing a multiplication of two floats. How come these statements produce different results ? Should I even be using floats ?

500,000.00 * 0.001660 = 830

Oddity Float Multiplications

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
bendigi
  • 590
  • 6
  • 15
  • 2
    show assignment to curPrinciple and periodicInterest – ondrejdee Jun 27 '13 at 07:50
  • After printing %.10f I realized there is plenty of decimal places in periodicInterest – bendigi Jun 27 '13 at 07:52
  • 4
    @H2CO3: The discrepancy is too big. This is not floating point error. – ondrejdee Jun 27 '13 at 07:54
  • showing assigmnet is not unnecessary because you get limited precision when you print float values – peko Jun 27 '13 at 07:59
  • Turned out to be that Periodic Interest was actually 0.00165975093841552734 and NOT 0.001660. I found out after I did a NSLog(@"%.20f"). – bendigi Jun 27 '13 at 08:01
  • @ondav How do you know? (And then what is it? Oh well, read the comment above mine... it **was** a floating-point error...) –  Jun 27 '13 at 08:10
  • 2
    @H2CO3 A `float` can represent at least seven significant decimal digits accurately - in other words, it can represent decimal numbers with accuracy of 1 part in 10^7 or better. Neither of the multiplicands in the question (500000 and 0.00166) have that many significant decimal digits. The error in the output is (830-829.875488)/830 ≈ 0.00015, or 1.5 parts in 10^6 - too large for a single floating point rounding error, since IEEE 754 requires multiplication to return the `float` that is nearest, correctly rounded, to the exact result. Thus the discrepancy is too big. – rob mayoff Jun 27 '13 at 08:20
  • @robmayoff I see, then **what** do you call this error? –  Jun 27 '13 at 08:20
  • I call it an error. Without seeing how he set `periodicInterest`, I can't be more specific. He set `periodicInterest` to something significantly different than 0.00166 and then expected it to behave as if he had set it to 0.00166. That is not a floating point error. – rob mayoff Jun 27 '13 at 08:22
  • @H2CO3 A format error. Printing a number of small magnitude with the `%f` conversion is liable to obscure the actual values by suppressing many significant digits. If the magnitude is smaller than `5e-7`, the printing leaves no trace of any significant digit. – Daniel Fischer Jun 27 '13 at 08:43

2 Answers2

5

How come these statements produce different results ?

Because floating-point arithmetic is not exact and apparently you were not printing the multiplier precisely enough (i. e. with sufficient number of decimal digits). And it wasn't .00166 but something that seemed 0.00166 rounded.

Should I even be using floats ?

No. For money, use integers and treat them as fixed-point rational numbers. (They still aren't exact, but significantly better and less error-prone.)

  • 1
    @Bathsheba Great minds think alike :) (I don't know of any other reasonable/sane answer to this question.) –  Jun 27 '13 at 07:50
  • 2
    The difference is too big to be caused "just" by floating point arithmetic. – ondrejdee Jun 27 '13 at 07:51
  • Re: "For money, use integers and treat them as fixed-point rational numbers", this is not going to be, conceptually, any better than using floats. Using fixed point rational numbers means that there is a constant resolution (e.g. for Q8 the resolution is 1/256), so you will be loosing precision just as with the floats. If numbers you will be using are rational and the operations on them preserve that (you do not do e.g. exp(x)) you could use any sort of arbitrary precision arithmetics, if you can accept the cons (I wouldn't but would instead use careful approach using either floats or Q). – ondrejdee Jun 27 '13 at 08:22
  • @ondav "I wouldn't but would instead use careful approach using either floats or Q" - so do you suggest that "don't use rational numbers, but use rational numbers"? –  Jun 27 '13 at 08:24
  • Technically, fixed-point rational with a base of 10^-7 would be enough for almost any real-world situation. There's no requirement (outside, perhaps, of efficiency) that the denominator be a power of 2. That said, in my opinion ([and others'](http://stackoverflow.com/questions/421463/should-i-use-nsdecimalnumber-to-deal-with-money)), the correct answer is to use `NSDecimalNumber`. – rob mayoff Jun 27 '13 at 08:25
  • @robmayoff Okay, thanks for your opinion (upvoted your answer because `NSDecimalNumber` seems to be the solution.) –  Jun 27 '13 at 08:27
  • 1
    One non-obvious benefit of using `NSDecimalNumber` is that it works with `NSNumberFormatter`, so you can let Apple take care of formatting currencies for all sorts of foreign locales. – rob mayoff Jun 27 '13 at 08:27
  • @H2CO3: No I was just wondering what you find so much better on Q which floats miss. You say "1) don't use floats because they are not exact, 2) use fixed point rationals". SO readers could easily imply that Qs are exact, which they are not. So, I suggested a solution if someone wanted exactness, to complement your answer. – ondrejdee Jun 27 '13 at 08:30
  • @ondav Okay, I see. I think you may want to add an answer as well, you provided valuable insights. –  Jun 27 '13 at 08:33
3

You didn't show how you initialized periodicInterest, and presumably you think you set it to 0.00166, but in fact the error in your output is large enough that you must not have explicitly initialized it as periodicInterest = 0.00166. It must be closer to 0.00165975, and the difference between 0.00166 and 0.00165975 is definitely large enough not to just be a single floating-point rounding error.

Assuming you are working with monetary quantities, you should use NSDecimalNumber or NSDecimal.

One non-obvious benefit of using NSDecimalNumber is that it works with NSNumberFormatter, so you can let Apple take care of formatting currencies for all sorts of foreign locales.

UPDATE

In response to the comments:

  • periodicInterest is clearly not a monetary quantity” and “decimal is no more free of error when dividing by 12 than binary is” - for inexact quantities, I can think of two concerns:

    • One concern is using sufficient precision to give accurate results. NSDecimalNumber is a floating-point number with 38 digits of precision and an exponent in the range -128…127. This is more than twice the number of decimal digits an IEEE 'double' can store. The exponent range is less than that of a double, but that's unlikely to matter in financial computing. So NSDecimalNumbers can definitely result in smaller error than floats or doubles, even though none of them can store 1/12 exactly.

    • The other concern is matching the results computed by some other system, like your bank or your broker or the NYSE. In that case, you need to figure out how that other system is storing numbers and computing with them. If the other system is using a decimal format (which is likely in the financial sector), then NSDecimalNumber will probably be useful.

  • “Wouldn't it be more efficient to use primitive types to do floating point arithmetic, specially thousands in real time.” Arithmetic on primitive types is far faster than arithmetic on NSDecimalNumbers. I haven't measured it, but a factor of 100 would not surprise me.

    You have to strike a balance between your requirements. If decimal accuracy is paramount (as it often is in financial programming), you must sacrifice performance for accuracy. If decimal accuracy is not so important, you can consider carefully using a primitive type, but you should be aware of the accuracy you're sacrificing. Even then, the size of a float is so small (usually only 7 significant decimal digits) that you should probably be using double (at least 15, usually 16 significant decimal digits).

    If you need to perform millions of arithmetic operations per second with true decimal accuracy, you might be able to do it using doubles, if you are an IEEE 754 expert capable of analyzing your code to figure out where errors are introduced and how to eliminate them. Few people have this level of expertise. (I don't claim to.) You must also understand how your compiler turns your Objective-C code into machine instructions.

    Anyway, perhaps you are just writing a casual app to compute a rough estimate of net present value or future value. In that case, using double would probably suffice, but using NSDecimalNumber would probably also be sufficiently fast. Without knowing more about the app you're writing, I can't give you more specific advice.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • I would like to vote for this answer, since it states the correct reason for the observed behavior. However, I do not support blanket advice to use `NSDecimal`. `periodicInterest` is clearly not a monetary quantity. The interest for a period is likely to be one-twelfth the annual interest, and decimal is no more free of error when dividing by 12 than binary is. Even if it were, compound interest can easily exceed the precision of a fixed-precision format, regardless of base. What is needed is understanding of floating-point arithmetic, so that software can be written in light of its issues. – Eric Postpischil Jun 27 '13 at 13:32
  • In the interest of picking a correct answer, this answer is most correct except for the NSDecimalNumber. I am not sure how efficient such method is. Wouldn't it be more efficient to use primitive types to do floating point arithmetic, specially thousands in real time. Rob, could you please explain or edit your answer before I vote for it ? By the way, the Periodic Interest was actually 0.00165975093841552734..... and NOT 0.001660. I found out after I tried a NSLog(@"%.20f"). – bendigi Jun 27 '13 at 15:04