Research Software Engineering at Warwick
https://blogs.warwick.ac.uk/researchsoftware
We are Research Software Engineering at the University of Warwick. Doing our daily job brings us up against odd stuff to do with computers and programming. We document them here so that we can say that we leave them a bit better than we found them.en-GB(C) 2024 Christopher Bradyhttps://blogs.law.harvard.edu/tech/rssChristopher BradyChristopher BradyWarwick Blogs, University of Warwick, https://blogs.warwick.ac.uk120SOUP: Function Pointers by Heather Ratcliffe
https://blogs.warwick.ac.uk/researchsoftware/entry/snippet_of_the_/
<p>Today's snippet demos function pointers (objects in Python), in particular an array of function pointers. We use them to print a table of arithmetic operations on all of the IEEE special values. </p>
<p><a href="https://en.wikipedia.org/wiki/IEEE_754">IEE</a><a href="https://en.wikipedia.org/wiki/IEEE_754">E 754</a> defines the behaviour of floating point numbers, in particular what happens when numbers get unrepresentable, whether that is too large, too small or plain not-numbers. </p>
<p>Infinity is a familiar enough concept and in floating-point it mostly means a number which is too large to be represented. There's a positive and a negative infinity and most arithmetic works as expected. </p>
<p>Negative zero (-0.0) seems quite odd at a first glance. The sign-bit is set, but in every other way, and in general, it is equal to positive zero. Comparisons like `0.0 == -0.0` are defined to be true and `-0.0 < 0.0` is false. Most languages have a function to copy the sign from one number to another though, and -0.0 works as expected. </p>
<p>NaN, Not a Number, mostly appears as a lament, "Why is it NaN!?" or worse "Why is it NaN again!?" Any operations involving NaN also give NaN as do several others. </p>
<p>It seems strange that `Inf * 0.0` or `Inf + -Inf` both give NaN where they could both reasonably give zero. Philosophically, both are numbers, but completely unknown ones. Inf isn't mathematical infinity, it is just too large to represent, and 0.0 stands in for any number too small to represent. Their product then can be absolutely anything, hence defining it as NaN. </p>
<p>Code snippets in C, Fortran and Python are in our <a href="https://github.com/WarwickRSE/SOUP/tree/master/001_IEEE" target="_blank">SOUP repo under 001_IEEE</a>. All three use named functions to "<a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">dry out</a>" the code: in fact they use an array of them. Note in Fortran this needs us to use a custom type to hold the function pointer, as we can't have an array of simply pointers. </p>
<p>The core of all snippets is the loop over operations and inputs</p>
<p>In C:</p>
<pre> float (*op)(float, float);/*Holds operation function*/
float(*allOps[4])(float, float);
allOps[0] = &add; allOps[1] = &sub; allOps[2] = &mul; allOps[3] = &div;
for(opNum=0; opNum< 4; opNum++){
op = allOps[opNum];
for(rowNum = 0; rowNum < 7; rowNum++){
row = allRows[rowNum];
/*print result of op(row, column)*/
}
}</pre>
<p>In Fortran (where f_ptr is our type holding a pointer):</p>
<pre> TYPE(f_ptr), DIMENSION(4) :: allOps
TYPE(f_ptr) :: currOp
allOps(1)%ptr => add
allOps(2)%ptr => sub
allOps(3)%ptr => mult
allOps(4)%ptr => div
DO opNum = 1, 4
currOp%ptr => allOps(opNum)%ptr
DO rowNum = 1, 7
row = allRows(rowNum)
!Print results of currOp%ptr applied to row, column
ENDDO
ENDDO
</pre>
<p>And in Python (using range-based for loops on lists):</p>
<pre> allOps = [add, sub, mul, div]
for opNum in range(0, 4):
op = allOps[opNum]
for rowNum in range(0, 7):
row = allRows[rowNum]
#Print result of op(row, column)</pre>
<p>Note all three are subtly different in how one assigns the active operation. In C you just get a pointer to a function with the usual address-of operator, &. In Fortran you point your pointer to the proper target using the points-to operator, =>. In Python we just set the operator to equal the function, omitting the brackets () turning this into an assignment, not a call. </p>
<p>Function pointers also have a powerful use in High-Performance code. A long if-else chain inside a loop, which calls a different function in each branch, can be replaced with the same if-chain outside the loop setting a function pointer and then a simple call inside the loop, eliminating the branch. As pseudo-code:</p>
<pre> DO i = 0, 10000
IF (condition) function_1()
ELSE IF (condition) function_2()
ELSE IF (condition) function_3()
...
ENDDO
</pre>
<p>becomes</p>
<pre> IF (condition) ptr = function_1
ELSE IF (condition) ptr = function_2
ELSE IF (condition) ptr = function_3
...
DO i = 0, 10000
ptr()
ENDDO
</pre>CodeHpcPointerSoupWed, 21 Feb 2018 19:19:02 GMTHeather Ratcliffehttps://blogs.warwick.ac.uk/researchsoftware/entry/snippet_of_the_/#comments8a1784e6600341de01607418531002040