Monday, December 3, 2012

Ternary Operator ?

Suppose you had a variable x that has a value depends on the variables a and b. The most obvious choice is to write an if-else statement to determine what x is:
if(a > b) then
   x = a
else
   x = b
endif

But this seems a bit cumbersome, particularly when you look at a more compressed form using the ternary operator ? as in C (among other languages):
x = a > b ? a : b;

I used to be jealous of C because they had the inline-if and I believed that Fortran did not. Fortunately for us, Fortran added this inline-if into the 1995 standard. Unfortunately, they chose the most random of intrinsic function names I have ever heard of: merge. It is used quite like the above:
x = merge(a,b,a>b)

More explicitly,
variable = merge(value if true, value if false, condition)

The following is a snippet from a simulation I wrote that involved a Monte Carlo sampling of positions in cylindrical coordinates. R,P,Z are the rho-phi-zed coordinate-arrays.
do i=1,numPoints
   call random_number(rTemp)
   call random_number(pTemp)
   call random_number(zTemp)
   call random_number(cTemp)
   R(i) = sqrt(rTemp)*rScale
   P(i) = 2.*pi*pTemp
   Z(i) = merge(zTemp*zScale,-zTemp*zScale, cTemp > 0.5)
enddo

A couple notes on the above:
(1) Fortran uses 1-index, rather than C's 0-index--which leads me to the aside: quickly count to 10. Did you start at 0? Fortran can actually start at 0, if you want. You would have to define the array to start there by using
real,dimension(0:numPoints)::myArray

(2) Fortran uses curved braces for the array index, whereas C uses the square braces for the same thing.
(3) The variable pi is not an intrinsic (why not!?) and needs to be declared beforehand; I find the easiest option is using pi=acos(-1.0), though others may suggest pi=4*atan(1.0) (which is fine, but it seems to me that one operation is better/faster than two).
(4) The intrinsic subroutine random_number(variable) was used because it is superior to the intrinsic function rand(). The former has a period of 2123 (~1037) while the latter has a period of (I think) 232 (~109).


Comments always welcome.

5 comments:

  1. pi=acos(-1.0) is superior to pi=4*atan(1.0) because the latter loses two bits of precision.

    ReplyDelete
  2. Shouldn't line 3 read
    "else if (a < b) then" ?
    For equality, the merge condition evaluates to false

    ReplyDelete
  3. +MartinDiehl: Actually, I think it should just be "else" in line 3 because we're testing for strictly greater than with merge and the C ternary operator. Using the "else if" there would eliminate equality (which should evaluate to false in the three cases, if they are to be equivalent).

    ReplyDelete
  4. It should be noted that in contrast to C, both arguments of merge function are calculated (or can be calculated, depending on a compiler), and you'll probably get a problem if one of them throws an exception even if this argument is not used according to the mask. So, the following call: merge(array(index), array(-index), index > 0) being innocent at first sight, can crash your program.

    ReplyDelete
  5. This blog awesome and i learn a lot about programming from here.The best thing about this blog is that you doing from beginning to experts level.

    Love from

    ReplyDelete