francis@csli.Stanford.EDU (Dave Francis) (11/29/90)
Hello netters! I have a roundoff problem when using floating point numbers in Think C 4.0.2. I've written a function which takes a floating point number and returns a string that can be used for displaying with QuickDraw. The problem is that when I pass the value to be converted Think C changes it to a less accurate number so when I generate the string I don't get exactly what was passed in. Here's a example: The number input is 2.314 but inside the routine it becomes 2.3139996765 or something like that (I think becuase Think C passes floating point as extended). Anyway when I generate the string the return is "2.313" if the precision is set to 3 (the function also takes a precision parameter) It gets worse if I pass a value which has less significant digits than the precision. So the value 3.2 passed in with a precision parameter of 3 in some cases gets returned as "3.199", which is unexceptable for display purposes if the user e ntered 3.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? Help!! Dave Francis Sapphire Design Systems
252u3129@fergvax.unl.edu (Mike Gleason) (11/29/90)
I solved a similar problem by changing my floating point variables from pascal REALs to EXTENDEDs. Try changing any floats or doubles to long doubles. -- mike gleason, unemployed student looking for employment. -- 252u3129@fergvax.unl.edu -- Go Georgia Tech!
ech@cbnewsk.att.com (ned.horvath) (11/30/90)
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...