Something about float point math – function call vs. operator

This post is triggered by a question of Python – “Is there any difference between pow() and ** operator?”. Similarly, this question applies to C and Java – “Is there any difference between pow(1, 2) and 1*1?”. The first thing came into my mind is the function call cost comparing with the operator. One could use the operator rather than the function call to improve the general performance. However, then why would function call exists? The answer is for better precision.

0. why doesn’t GCC optimize a*a*a*a*a*a to (a*a*a*)*(a*a*a)?

This StackOverflow post (http://stackoverflow.com/questions/6430448/why-doesnt-gcc-optimize-aaaaaa-to-aaaaaa) talks about why the compiler would not optimize such expression to an apparently better one (with less assembly instructions). The take-away behind this is Float Point Math is NOT Associative.

1. function call vs operator

We will run the simple code below to see what will happen in C, Python and Java. Note that the testings are run on my x86-64 MacBook Pro. The compilers are GCC(LLVM 5.0), Python 2.7.5 and JDK 1.7.0_40.

C:

/*
* float_number.c
* To verify that Float Point Math is NOT Associative
* Mar 2, 2014
* root@davejingtian.org
* http://davejingtian.org
*/

#include <stdio.h>
#include <math.h>

int main(void)
{
float f;
for (f = 1; f < 2; f += 0.1)
{
printf(“——————————\n”);
printf(“pow(%f, 6)=%f\n”, f, pow(f, 6));
printf(“%f**6=%f\n”, f, f*f*f*f*f*f);
printf(“(%f**3)*(%f**3)=%f\n”, f, f, (f*f*f)*(f*f*f));
}
return 0;
}

Part of output:
——————————
pow(1.900000, 6)=47.045913
1.900000**6=47.045914
(1.900000**3)*(1.900000**3)=47.045918

Don’t be surprise if the results are not the same as above. pow() function gives the best precision comparing with the raw operator. If we change the ‘float f’ to ‘double f’, given the length of the decimal, the output are all the same. However, this does not mean they are the same indeed. Double type just provides a better precision during the computation and shows the same results after rounding.

Python:

# float_math.py
# An example file to show that
# Float Point Math is NOT Associative
# Mar 2, 2014
# root@davejingtian.org
# http://davejingtian.org

import math

float_num = [1., 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.]

for f in float_num:
print(“————————————“)
print(“pow(%f, 6)=%f” %(f, pow(f, 6)))
print(“%f**6=%f” %(f, f**6))
print(“(%f**3)*(%f**3)=%f” %(f, f, (f**3)*(f**3)))

Part of output:
————————————
pow(1.900000, 6)=47.045881
1.900000**6=47.045881
(1.900000**3)*(1.900000**3)=47.045881

Interesting, the output are all the same. What happens here? Well, as I am using default C python as the PVM. The float type in Python is mapped into double in C. It has the same output in C with double type. Other PVM may have different output.

Java:

/*
* float_number.java
* An example to verify Float Point Math is NOT Associative
* Mar 2, 2014
* root@davejingtian.org
* http://davejingtian.org
*/

public class float_number
{
public static void main(String args[])
{
float f;
for (f = 1; f < 2; f += 0.1)
{
System.out.println(“————————“);
System.out.println(“pow(“+f+”, 6)=”+Math.pow(f, 6));
System.out.println(f+”*”+f+”*”+f+”*”+f+”*”+f+”*”+f+”=”+(f*f*f*f*f*f));
System.out.println(“(“+f+”*”+f+”*”+f+”)*(“+f+”*”+f+”*”+f+”)=”+((f*f*f)*(f*f*f)));
}}}

Part of output:
————————
pow(1.9000002, 6)=47.04591287880129
1.9000002*1.9000002*1.9000002*1.9000002*1.9000002*1.9000002=47.045914
(1.9000002*1.9000002*1.9000002)*(1.9000002*1.9000002*1.9000002)=47.045918

Again, similar output like float in C. If double is used, it looks like below. Again, the key point here is double is better but not exactly the same!

————————
pow(1.7000000000000006, 6)=24.137569000000052
1.7000000000000006*1.7000000000000006*1.7000000000000006*1.7000000000000006*1.7000000000000006*1.7000000000000006=24.13756900000005
(1.7000000000000006*1.7000000000000006*1.7000000000000006)*(1.7000000000000006*1.7000000000000006*1.7000000000000006)=24.137569000000045
————————
pow(1.8000000000000007, 6)=34.01222400000008
1.8000000000000007*1.8000000000000007*1.8000000000000007*1.8000000000000007*1.8000000000000007*1.8000000000000007=34.01222400000008
(1.8000000000000007*1.8000000000000007*1.8000000000000007)*(1.8000000000000007*1.8000000000000007*1.8000000000000007)=34.01222400000008
————————
pow(1.9000000000000008, 6)=47.04588100000012
1.9000000000000008*1.9000000000000008*1.9000000000000008*1.9000000000000008*1.9000000000000008*1.9000000000000008=47.04588100000012
(1.9000000000000008*1.9000000000000008*1.9000000000000008)*(1.9000000000000008*1.9000000000000008*1.9000000000000008)=47.04588100000012

2. conclusion

For precision, functions calls (provided by the corresponding Math lib) usually are better than the raw operators. And this is the reason why there are such function calls. Also, double type is better than the float indeed. For performance, operators should be better as without function cost. For optimization, because of Float Point Math in computers is not always equal to the one in reality, compilers usually stick with the user’s input rather than ‘finding a better way’. Like mentioned in the StackOverflow post, if (a*a*a)*(a*a*a) is what you want, write it in that way.

About daveti

Interested in kernel hacking, compilers, machine learning and guitars.
This entry was posted in Programming, Stuff about Compiler. Bookmark the permalink.

1 Response to Something about float point math – function call vs. operator

  1. archive.org says:

    I’m a coder and I’ve just developed a revolutionary internet dating internet site.
    I will be searching for beta evaluators to search and try it out.
    Do you wish to opt-in? We are able to pay
    you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.