Name: cool_andy Date: December 11, 2007 at 02:44:01 Pacific Subject: Slight inaccuracy Rounding Problem OS: Windows XP CPU/Ram: 512 Model/Manufacturer: Acer
Comment:
Ok I have implemented the following function to round figures ".5" up instead of down. float Round_float(float value) { char roundtest[40]; char *position = roundtest;
value += 0.00500001;
sprintf(roundtest,"%lf",value);
while ( *position != 0 ) { if ( *position == '.') { *(position + 3) = 0; /*replace 3rd decimal place with null*/ break; }
position++; }
return atof(roundtest);
}
This returns a 2.d.p figure. To test it I have done the following: float test = 11.5549999;
test = Round_float(test); printf("test = %.2f\n",test);
The figure is 11.56 in error. It should have been 11.55. However if the test variable was 1 decimal place less than above 11.554999 (6.d.p). The figure is rounded to 11.55 correctly.
In my program I have many net price percentage increases which at the moment appear to be correctly rounded. Although the scenario above is unlikely to occur, I am justing wonder how to solve the slight inaccuracy above (7.d.p or more).
I have also just realised that 11.549999 plus 0.00500001 becomes 11.56. So only 0.005 is needed to be added on in this scenario case?
If you want to round up to 2 decimal points then just add .005 in all circumstances.
.5 will round up the nearest integer with the fraction discarded. .05 to one decimal point with digits over one discarded. .005 to two decimal points with digits over two discarded. .0005 to three decimal points with digits over three discarded.
Your method of truncating the non-significant digits, by converting to a string and back again, is very compute-intensive. Better to multiply by 100, 1000, etc., then truncate to its integer part using a suitable run-time library function, and divide by 100 or 1000. Also, instead of adding .5 and truncating, why not use the round() function?
Ok, if I add on just 0.005 to 11.5549999 its becomes 11.560000. I would expect it to become 11.5599999 before truncating. Is this possibly because floating point figures have a limit of 6.d.p? "%f" always displays six decimal figures.
Also I do not have access to trunc() and round() in the math.h library. I am using C. Are they standard C++ library functions? Alternatively it would be useful to know a different truncate programming method than removing the 3rd decimal place in the string as above.
When you add 0.005 to 11.5549999, it does not become 11.56000. It just appears to, when you print it. Floating-point numbers are not represented the same way internally as they are when printed in base 10.
The "%f" format can display to any precision you like; just specify it. If you want 10 dp, use "%.10f" It only prints 6dp by default because it's unusual that you would want more than than in typical circumstances.
The trunc() and round() functions are part of the ISO Standard C 99 language. If you are using a Microsoft compiler, it's unlikely that they've implemented that standard yet; you are probably still using the ISO C 90 standard. You can round the number to 2 dp by using this expression:
(f + 0.005) - fmod(f + 0.005, 0.01)
Alternatively, and a little closer to the method I mentioned in my earlier post:
Ok after using both my string method and your method above. I still get the answer 11.56 rounded from 11.5549999 incorrectly. If I use printf to specify 7.d.p "%.7f" it displays (after adding 0.005): 11.5600004. For some reason that appears to be an error margin of 0.0000005 (7.d.p). Simliarly if I use printf to specify 8.d.p "%.8f" it displays: 11.56000042 (error margin = 0.00000052)
I just trying to understand why these figures above are appearing using printf. However I only consider this a minor issue, and you have helped me learned a little more about rounding. Thanks again for your advice Andrew
I get 11.55999990, as you would expect. You get 11.56 when you printf("%f",...) because that rounds the displayed value to 6dp.
I have no idea why you get 11.5600004. That's strange. Could it be that you are sprintf'ing to 6dp and then converting back to 7dp, thus losing significant figures?
You want it rounded up to 2 decimal points which is what you are getting.
You will never get it exact when you are up to eight decimal points because of the vagaries of floating point binary arithmetic. Small errors will always creep in and .00000052 is a very small error. A little over five millionth.
I think I understand the problem, I suspect this is because the way floating point numbers are handled in binary arithemtic as you mention above. I noticed the webpages Klint posted above "What every computer scientist should know about floating-point arithmetic". Those articles look pretty complicated and difficult to understand.
However I do have one query following klint's last reply. printf("%12.8f", 11.5549999 + 0.005); It prints - 11.554999990, correctly
But if a variable is used after adding: valye = 11.5549999; value += 0.005; printf("%12.8f",value); It prints 11.56000042
I presume that is the way printf is handling the floating point figure?
>But if a variable is used after adding: >valye = 11.5549999; >value += 0.005; >printf("%12.8f",value); >It prints 11.56000042 > >I presume that is the way printf is >handling the floating point figure?
No, this seems to be losing some accuracy. Are you using float? That doesn't hold that many decimal places, try using double. These days, with processors' built-in ability to do 80-bit floating point arithmetic, there is not much point in using float, unless you have a huge array of them and you want to use less memory.
Hi, If I declare "value" as a double variable, I get the following (8.d.p): 11.56000031
Slightly less error margin than using a float figure. It seems if any figure is over 6.d.p close to the half way point, very small errors always occur. Despite this I am happy with the useful advice you have given me about rounding floating point figures. Thanks for your time.
Using Visual Studio 2003, when I run the following program (copy&paste exact verbatim copy):
#include <stdio.h>
int main() { double value = 11.5549999; value += 0.005; printf("%12.8f", value); return 0; }
... it prints 11.55999990, just as expected. Can you copy & paste your complete application and tell me what it prints, and I'll try with my compiler to see if there is a difference.
Ok I have managed to solve the problem. I made a silly mistake by forgeting to change the actually declared variable which was still float. This float variable "value" was being passed into round_double(double value) function, thus still giving an error margin.
I have also discovered the string method I created, is still rounding to 11.56 incorrectly. But the method you mentioned above returns 11.55 correctly from 11.55999990 (f + 0.005) - fmod(f + 0.005, 0.01)
fmod gets the remainder after 2.d.p, which is 0.0099999. Therefore 11.5599990 - 0.0099999 = 11.55
That seems to have done trick, thank you very much. Mission accomplished :) I just have to remember to use double variables instead of float especially when my program contains a lot of discounts and nett prices stored in a linked list.
If you are working with currency amounts, you may be better off using long int to represent cents (or pence, or whatever your minor unit is.) Integral types have exact representations, rather than being subject to the pitfalls of floating-point.
The information on Computing.Net is the opinions of its users. Such
opinions may not be accurate and they are to be used at your own risk.
Computing.Net cannot verify the validity of the statements made on this site. Computing.Net and Computing.Net, LLC hereby disclaim all responsibility and liability for the content of Computing.Net and its accuracy.
PLEASE READ THE FULL DISCLAIMER AND LEGAL TERMS BY CLICKING HERE