ethangk
ethangk

Reputation: 109

Rearranging an equation

I have the following equation in my C code

k * dl * (1.0 + pHold / centre
       + (pHold * pHold) / (2.0 * centre * centre)
       - square / (2.0 * centre))

I know that floating point divisions are a lot more expensive than multiplications, and I have been wrestling with this for a while. Is there any way to rearrange this to cut out a division?

Thanks

Upvotes: 1

Views: 377

Answers (7)

LihO
LihO

Reputation: 42133

Just note that before you actually try to optimize some part, you should:

  • make sure that it is correct
  • make sure that there is no way how to optimize this at the higher level
    ~ Isn't my program invoking this calculation more times than it is actually needed?
    ~ Could I use the previous results? (What is dynamic programming?)
  • once you know where is the bottleneck, bench-marking should follow:
    ~ It seems to be slow... How "slow" is it? ... How "fast" should it become?

But in case you are sure that the equation itself should be optimized, you could use the fact that the multiplicative inverse of centre appears in your equation 4 times, reducing the count of divisions to 1:

double centreInv = 1.0 / centre;
double pHoldToCentre = pHold * centreInv;
double result = 
    k * dl * (1.0 + pHoldToCentre 
              + 0.5 * pHoldToCentre * pHoldToCentre 
              - 0.5 * square * centreInv);

Also note that these kind of changes might actually affect the result of this equation, so if you decide to change it, make sure it still produces desired output.

Upvotes: 7

Jonathan Leffler
Jonathan Leffler

Reputation: 754800

Algebraically, you can reduce it to a single division. Using:

  • k for k
  • d for dl
  • p for pHold
  • c for centre
  • s for square

your equation is:

           p     p.p     s
k.d ( 1 + --- + ----- - --- )
           c    2.c.c   2.c

which transforms to:

k.d ( 2.c.c + 2.c.p + p.p - c.s )
---------------------------------
             2.c.c

and hence to

k.d (2.c (c + p) - c.s + p.p)
-----------------------------
            2.c.c

Or, in terms of your original variables:

(k * dl * (2 * centre * (centre + pHold) - centre * square + pHold * pHold)) /
                    (2 * centre * centre)

Whether that is as good numerically as the original equation is a separate discussion. To discuss that, we'd need to know the typical ranges for each of the terms in the equation (and even then, my brain would hurt).

Upvotes: 1

ksu
ksu

Reputation: 608

Hi I have no Idea of Programming C :)

But given k, dl, pHold, centre and square are all variables, you can simplify this mathematical equation to:

  k*dl*(2.0* centre * centre + 2.0 * centre * pHold - centre *square + pHold * pHold)
  /  (2.0 * centre * centre)

substitute variables to single character variables and use http://www.wolframalpha.com

edit: Nikos C has basically the same answer but is factoring out 2c. You can test/chose which one performs better.

Upvotes: 0

Jerry Jeremiah
Jerry Jeremiah

Reputation: 9643

If you look at the denominators for the fractions you can see that making a common denomination would allow you to do the division just once (at the expense of more multiplications):

k * dl * (1.0
  + pHold                  / (centre)
  - square                 / (2.0 * centre)
  + (pHold * pHold)        / (2.0 * centre * centre)
)

If you are sure that a floating point multiplication is better than a floating point division then:

k * dl * (1.0
  + (pHold * 2.0 * centre) / (2.0 * centre * centre)
  - (square * centre)      / (2.0 * centre * centre)
  + (pHold * pHold)        / (2.0 * centre * centre)
)

Which becomes:

k * dl * (1.0
  + ( (pHold * 2.0 * centre)
    - (square * centre)
    + (pHold * pHold) )     / (2.0 * centre * centre)
)

Upvotes: 4

Nikos C.
Nikos C.

Reputation: 51910

You can reduce this to only one division overall:

k * dl * (2 * centre * (centre + pHold) + pHold * pHold - centre * square)
/ (2.0 * centre * centre)

Upvotes: 0

damienfrancois
damienfrancois

Reputation: 59290

In the old days, you probably would have written

oocenter = 1/center; 

and used it in the expression

k * dl * (1.0 + pHold * oocentre
       + pHold * pHold * 0.5 * oocentre * oocentre
       - square * 0.5 * oocentre)

Nowadays, I believe the compilers are smart enough to do that for you. I would suggest working towards vectorization and parallelization.

Upvotes: 0

caf
caf

Reputation: 239301

You cn cut out at least one:

k * dl * (1.0 + (pHold
       + (pHold * pHold) / (2.0 * centre)
       - square * 0.5) / centre)

Upvotes: 0

Related Questions