Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a preferred way to order floating-point operands?

Tags:

Suppose I have a very small float a (for instance a=0.5) that enters the following expression:

6000.f * a * a;

Does the order of the operands make any difference? Is it better to write

6000.f * (a*a);

Or even

float result = a*a;
result *= 6000.f;

I've checked the classic What Every Computer Scientist Should Know About Floating-Point Arithmetic but couldn't find anything.

Is there an optimal way to order operands in a floating point operation?

like image 899
lindelof Avatar asked Oct 28 '11 22:10

lindelof


2 Answers

It really depends on the values and your goals. For instance if a is very small, a*a might be zero, whereas 6000.0*a*a (which means (6000.0*a)*a) could still be nonzero. For avoiding overflow and underflow, the general rule is to apply the associative law to first perform multiplications where the operands' logs have opposite sign, which means squaring first is generally a worst strategy. On the other hand, for performance reasons, squaring first might be a very good strategy if you can reuse the value of the square. You may encounter yet another issue, which could matter more for correctness than overflow/underflow issues if your numbers will never be very close to zero or infinity: certain multiplications may be guaranteed to have exact answers, while others involve rounding. In general you'll get the most accurate results by minimizing the number of rounding steps that happen.

like image 54
R.. GitHub STOP HELPING ICE Avatar answered Oct 01 '22 16:10

R.. GitHub STOP HELPING ICE


The optimal way depends on the purpose, really.

First of all, multiplication is faster than division.

So if you have to write a = a / 2;, it is better to write a = a * 0.5f;. Your compiler is usually smart enough to replace division with multiplication on constants if the results is the same, but it will not do that with variables of course.

Sometimes, you can optimize a bit by replacing divisions with multiplications, but there may be problems with precision.

Some other operations may be faster but less precise. Let's take an example.

float f = (a * 100000) / (b * 10);
float g = (a / b) * (100000 / 10);

These are mathematically equivalent but the result can be a little different. The first uses two multiplication and one division, the second uses one division and one multiplication. In both cases there may be a loss in precision, it depends on the size of a and b, if they are small values first works better, if they are large values second works better

Then... if you have several constants and you want speed, group contants together.

float a = 6.3f * a * 2.0f * 3.1f;

Just write

a = a * (6.3f * 2.0f * 3.1f);

Some compiler optimize well, some other optimize less, but in both cases there is no risk in keeping all constants together.

After we say this we should talk for hours on how processors works. Even the same family like intel works in a different way between generations! Some compilers uses SSE instructions, some other doesn't. Some processor supports SSE2, some SSE, some only MMX... some system don't have an FPU neither! Each system do better some calculations than other, finding a common thing is hard.

You should just write a readable code, clean and simple, without worryng too much about these unpredictable very low level optimizations.

If your expression looks complicated, do some algebra and\or go to wolframalpha search engine and ask him to optimize that for you :)

Said that, you don't really need to declare one variable and replace its content over and over, compiler usually can optimize less in this situation.

a = 5 + b;
a /= 2 * c;
a += 2 - c;
a *= 7;

just write your expression avoiding this mess :)

a = ((5 + b) / (2 * c) + 2 - c) * 7;

About your specific example, 6000.f * a * a, just write it as you write it, no need to change it; it is fine as it is.

like image 29
Salvatore Previti Avatar answered Oct 01 '22 16:10

Salvatore Previti