Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!wuarchive!zaphod.mps.ohio-state.edu!rpi!bu.edu!att!cbnewsl!cbnewsk!ech From: ech@cbnewsk.att.com (ned.horvath) Newsgroups: comp.sys.mac.programmer Subject: Re: Floating Point accuracy in Think C Message-ID: <1990Nov29.194300.22859@cbnewsk.att.com> Date: 29 Nov 90 19:43:00 GMT References: <16594@csli.Stanford.EDU> Organization: AT&T Bell Laboratories Lines: 78 From article <16594@csli.Stanford.EDU>, by francis@csli.Stanford.EDU (Dave Francis): > I have a roundoff problem when using floating point numbers in Think C > 4.0.2... > Is there a way to stop this loss of accuracy when passing floating point > values? Why is there a loss of accuracy anyway? What should I do? Are > there any rounding functions out there or better float->string converters? Floating point numbers always experience SOME loss of accuracy: consider expressing 1/3 as a decimal number: the sequence of threes is periodic, but infinite, and so there's always a bit of loss. It ain't the Mac, and it ain't SANE, and it ain't a ThC problem. This can't turn into a numerical methods lecture (course, career, ...) but suffice to say that some algorithms are much more sensitive ("unstable") to rounding error than others, so depending on how you manipulate the numbers, you can get good or bad results. Look in the (book) library under numerical analysis: authors like Foreman Acton and Webb Miller will help you find good (i.e. less sensitive) algorithms. At display time, use an old APL trick, the "fuzz." Decide what level of accuracy is important to you, then add a bit to your result (or subtract a bit if the value is negative) to allow rounding in the right direction. If you plan to display answers like 99.999, add .0005 before using sprintf. If you're dealing with numbers with limited accuracy (like currency, interest rates correct to 3 places, etc.) then you might consider avoiding floating point altogether, using a fixed-point scheme, i.e. holding your numbers as (long?) integers with an implicit scale factor. Not only will you have better accuracy, you'll have much better performance. The Mac Toolbox Utilities support a particular flavor of fixed-point (Fixed) -- see IM 1-467 -- with the binary-point in the middle of a 32-bit word. To display one of these using sprintf, and three digits to the right of the decimal point: Fixed val; ... sprintf (buffer, "%d.%d", (int) (val >> 16), /* integer part */ FixRound (labs(val) * 1000) % 1000 /* decimal part */ ); Using Fixed will still result in rounding error when you use FixRatio to convert decimal to Fixed. An alternative approach, if you are dealing with a fixed number of decimal digits, e.g. currency, is to hold an amount of money as an integer in terms of thousandths-of-a-dollar. Then, when you go to display it, use something like typedef long Bucks; Bucks val; sprintf (buffer, "$%d.%d", val/1000, /* dollars */ val%1000/10); /* cents */ Now, when you want to add such numbers, just do it. To multiply, remember that you have to renormalize: in this example, multiplying two of these numbers gives us millionths of a dollar, so we have to post-divide by 1000 to get back to thousandths: Bucks a, b; ... a = (a*b)/1000; or, to take a percentage, say 8.5 percent: b = (a*85)/1000; /* 8.5% == .085 == 85/1000 */ The bottom line -- and it's not for everyone! -- is that if you think about your problem a bit, and work a bit harder, you can get high accuracy AND high speed by avoiding floating point. If the round-up-before-display works for you, and the performance is adequate for your target audience, then just ignore all this stuff. At the risk of being tarred and feathered, COBOL handles this kind of thing really well... =Ned Horvath= ehorvath@attmail.com Disclaimer: I haven't programmed COBOL since '71. But I'd use it if I had the right kind of problem...