Reputation: 1972
An obvious way to do this would be the following:
integer function log2_i(val) result(res)
implicit none
integer, intent(IN) :: val
if (val<0) then
print *, "ERROR in log2_i(): val cannot be negative."
else if (val==0) then
print *, "ERROR in log2_i(): todo: return the integer equivalent of (-inf)..."
else if (val==1) then
res = 0
else
res = FLOOR( LOG(val+0.0d0)/LOG(2.0d0) )
endif
end function log2_i
Is there a better way using Fortran's bitshifting operators?
This question is almost the same, but uses unsigned integers. Unfortunately, the sign bit would prohibit using the same algorithm.
Upvotes: 2
Views: 1421
Reputation: 18118
As @harold mentioned, this shouldn't be a problem: since the logarithm is only defined for positive numbers, the sign bit is always zero (see the corresponding Wikipedia article). Therefore, the algorithm in the linked answer can be directly ported to Fortran (2008 Standard):
module mod_test
contains
function ilog2_b(val ) result( res )
integer, intent(in) :: val
integer :: res
integer :: tmp
res = -1
! Negativ values not allowed
if ( val < 1 ) return
tmp = val
do while (tmp > 0)
res = res + 1
tmp = shiftr( tmp, 1 )
enddo
end function
end module
program test
use mod_test
print *,'Bitshifting: ', ilog2_b(12345)
print *,'Formula: ', floor( log(real(12345) ) / log(2.) )
end program
This is a solution based on the Fortran 95 intrinsic BTEST
as suggested by @agentp:
module mod_test
contains
function ilog2_b2(val ) result( res )
integer, intent(in) :: val
integer :: res
integer :: i
res = -1
! Negativ values not allowed
if ( val < 1 ) return
do i=bit_size(val)-1,0,-1
if ( btest(val, i) ) then
res = i
return
endif
enddo
end function
end module
program test
use mod_test
print *,'Testing bits:', ilog2_b2(123456)
print *,'Formula: ', floor( log(real(123456) ) / log(2.) )
end program
Thanks to @IanH to point me towards bit_size
...
If your compiler supports shiftr
, I would use the first variant instead.
@IanH mentioned yet another method using leadz
, which is a Fortran 2008 feature:
module mod_test
contains
function ilog2_b3(val ) result( res )
integer, intent(in) :: val
integer :: res
res = -1
! Negativ values not allowed
if ( val < 1 ) return
res = bit_size(val)-leadz(val)-1
end function
end module
program test
use mod_test
print *,'Testing bits:', ilog2_b3(123456)
print *,'Formula: ', floor( log(real(123456) ) / log(2.) )
end program
Upvotes: 5