Allow math intrinsic functions to take integer arguments

In Julia, Matlab, R, and Python (after from math import sqrt, exp) you can write sqrt(2) or exp(2) and get the same result as for sqrt(2.0) and exp(2.0). Their default real is double precision. Currently the Fortran math intrinsics such as sqrt and exp do not accept integer arguments. It would be convenient if Fortran behaved like several of the other scientific programming languages and treated integer arguments of math intrinsics as double precision reals.

You often see square roots of integers in math formulas, so sqrt(2) may be closer than sqrt(2.0) to what the programmer intends. A compiler could use a lookup table for sqrt for small integer arguments. Writing sqrt(real(2,kind=dp)) is verbose.

One can of course define a module integer_math_mod and define functions such as sqrt and exp for integer arguments within it. Is this bad style?

1 Like

It’s also worth noting that for some operations (eg exp2) it is possible to get faster implementations for integer inputs.

Would you also want sqrt(-2) to use double precision complex? Questions like that also arise with operators. For example people peeved that integer/integer doesn’t do what they wanted might try overloading m//n to give real(m,kind=kind(1d0)/real(n,kind=kind(1d0), or invent .something. if they preferred the precedence of a user-defined operator to that of // . Of course Fortran won’t let you specify your own precedence. I used to specify the precedence of user-defined operators in Algol 68 until the people in charge of our computer system discarded its compiler decades ago.

Seems like a good way to introduce hard to track down bugs. If, for example, sqrt(2) defaults to double precision, you can have either excess precision or worse significant loss of precision due to mixed-mode arithmetic.

!
! Assume gfortran on x86_64.
!
program foo
   !
   ! Assume your sqrt(2) occurred here for rt2.
   !
   real(8), parameter :: rt2 = sqrt(dble(2))

   real(4) x4
   real(8) x8
   real(10) x10
   real(16) x16
   !
   ! This is effectively x4 = real(exp(2._4), 8) * rt2.
   !
   x4 = exp(2._4) * rt2
   print '(Z0)', x4
   print '(Z0)', exp(2._4) * sqrt(2._4)
   print *
   !
   ! Okay
   !
   x8 = exp(2._8)  * rt2
   print '(Z0)', x8
   print '(Z0)', exp(2._8) * sqrt(2._8)
   print *
   !
   ! This is effectively x10 = exp(2._10) * real(rt2, 10)
   ! Loss of precision: 64 vs 53-bits.
   !
   x10 = exp(2._10) * rt2
   print '(Z0)', x10
   print '(Z0)', exp(2._10) * sqrt(2._10)
   print '(A)', '-----------------^'
   print *
   !
   ! This is effectively x16 = exp(2._16) * real(rt2, 16)
   ! Loss of precision: 113 vs 53-bits.
   !
   x16 = exp(2._16) * rt2   ! Loss of precision: 113 vs 53-bits.
   print '(Z0)', x16
   print '(Z0)', exp(2._16) * sqrt(2._16)
   print '(A)', '-----------------^'
end program foo

% gfcx -o z -O a.f90 && ./z
412731FC
412731FC

4024E63F846B36C0
4024E63F846B36C0

4002A731FC2359B5FD6F
4002A731FC2359B5FA38
-----------------^

40024E63F846B36BFADE415529CEBA79
40024E63F846B36BF46F2435E0893810
-----------------^

Compilers would return NaN as they do for the following code:

n = 2
print*,sqrt(-dble(n))
end

For things such as SQRT(2.0), any decent compiler already evaluates this at compile-time and just substitutes a constant. I’m not seeing any significant advantage to either adding many more signatures or inventing some sort of conversion rule. It doesn’t actually add any functionality (you can always say REAL(int-value) as the argument.)

2 Likes