Crunching your abs

That min() and max() are highly related to each other probably doesn’t come as a surprise to many people. After all, we have some pretty obvious identities such as:

min(a,b) = -max(-a,-b)
max(a,b) = -min(-a,-b)

What may not be as immediately apparent is that both are also highly related to the absolute value function abs():

abs(a) = max(a,-a)
abs(a) = -min(a,-a)

In fact, it is possible to derive lots of identities relating min, max, and abs to each other. For example, from these two

a+b = max(a,b) + min(a,b)
abs(a-b) = max(a,b) - min(a,b)

we get that

a+b+abs(a-b) = 2*max(a,b)
a+b-abs(a-b) = 2*min(a,b)

which in turn gives us:

a+b = 2*min(a,b)+abs(a-b)
a+b = 2*max(a,b)-abs(a-b)
min(a,b) = (a+b-abs(a-b))/2
max(a,b) = (a+b+abs(a-b))/2

While this is perhaps mostly trivia for geeks like me and you, dear reader, some of these identities are actually useful. For example, if abs is available as a native instruction (as is often the case) but min and max are not, then (assuming you can live with the errors the arithmetic might introduce in e.g. floating-point) you can compute min or max of two values without using any comparisons or branches. (BTW, if I forgot to include your favorite identity above, share it in the comments!)

If we have expressions involving lots of min and max terms we sometimes can reduce the amount of mins and maxes we do through rewrite rules such as:

min(a,b) - c = min(a-c,b-c)
min(max(a,b),max(a,c)) = max(a,min(b,c))
min(max(a,b),max(a,b+k)) = max(a,b), if k >= 0
etc.

Clamping at a constant with min or max can be done with abs too:

max(k,a) = max(k-k,a-k)+k = max(0,a-k)+k = (a + k + abs(a-k))/2
min(k,a) = min(k-k,a-k)+k = min(0,a-k)+k = (a + k - abs(a-k))/2
max(0,a) = (a + abs(a))/2
min(0,a) = (a - abs(a))/2

These days we do a fair number of max(0,a) expressions in shader code (because we cannot use saturate to clamp HDR expressions as we don’t want to clamp to 1). On e.g. recent NVIDIA hardware both max(0,a) and (a + abs(a))/2 can be expressed in a single cycle, but I’m wondering if there exists situations where the latter expression is actually more efficient. Theoretically, the latter expression could allow for the three different operations to be executed with other instructions elsewhere “for free” (whereas the max instruction would never be free). Slim chance, yes, but still theoretically possible. Has anyone tried? If not, try it out, then share the results here!

Leave a Reply