Friday, October 2, 2015

More Floating Point Goodness

Continuing my look into floating point arithmetic, I decided to see how things work in different languages. If you didn't see my first foray into this, you can check that out here.

More info: What Every Computer Scientist Should Know About Floating-Point Arithmetic


Java:
 
double i = 0.1;  
double j = 0.2;
double n = i + j;
double x = 0.3;

System.out.println(n);
System.out.println(x);
System.out.println( (n == x) ? "true" : "false" );
Output:
 0.30000000000000004
 0.3
 false


Java has a data type made to deal with this type of issue: BigDecimal. It is known to be slow so you wouldn't use it in places where performance is an issue. I'm not saying this is the only way to do it either. It's just what I found during my investigations.

Java Fixed:
BigDecimal i = new BigDecimal(0.1);
BigDecimal j = new BigDecimal(0.2);
BigDecimal n = i.add(j).setScale(3,BigDecimal.ROUND_HALF_UP);
BigDecimal x = new BigDecimal(0.3).setScale(3,BigDecimal.ROUND_HALF_UP);

System.out.println(n);
System.out.println(x);
System.out.println( ( n.compareTo(x) == 0 ) ? "true" : "false" );
Output:
 0.300
 0.300
 true
Notice that we use setScale() to specify the number of digits after the decimal place and how to round them. When I did math in JavaScript, I used the same type of method, do the computation and then round the result a few places past the decimal point that way when we display it, the number will be more precise.



In C/C++, float is used for less precision, but appears to actually work correctly for some smaller calculations.

C/C++:
float i = 0.1;
float j = 0.2;
float n = i + j;
float x = 0.3;

printf("%f\n",n);
printf("%f\n",x);
printf("%s\n", (n == x)? "true" : "false" );
Output:
 0.300000
 0.300000
 true
When changing to double, we get the same output for the numbers from printf() but it appears that the values are stored differntly in memory when we compare them.

C/C++:
double i = 0.1;
double j = 0.2;
double n = i + j;
double x = 0.3;

printf("%f\n",n);
printf("%f\n",x);
printf("%s\n", (n == x)? "true" : "false" );
Output:
 0.300000
 0.300000
 false


Fixed C/C++:
printf("Math Test\n");
double i = 0.1;
double j = 0.2;
double n = i + j;
double x = 0.3;

printf("%f\n",n);
printf("%f\n",x);
printf("%s\n", compareDouble(n,x) ? "true" : "false");
Helper Function:
bool compareDouble(double a, double b) {   // include stdbool in C since bool isn't defined
 return fabs(a - b) < DBL_EPSILON;      // include math.h for fabs()
}
Output:
 0.300000
 0.300000
 true
In C, float.h is required to access DBL_EPSILON and in C++, cfloat.h is required.


As you can see, once we get down to C/C++, the way we deal with computations are more hands-on. You could set the epsilon manually and do the comparison against that to have total control over everything. Then we could take what we've learned here and use the same technique further up the ladder in the other languages instead of relying on other functions to do the work for us. I have to say it once again though, if you can find a library or some code where someone else has alreay done the math, then use it. I'm guessing most applications won't need the highest level of percision, but if your bank is dropping a cent from each transaction you make, that could really add up over the years.

No comments:

Post a Comment