Use CEILING Without the Effect of Rounding Error - fortran

I'm trying to use the intrinsic function ‘CEILING’, but the rounding error makes it difficult to get what I want sometimes. The sample code is just very simple:
PROGRAM MAIN
IMPLICIT NONE
INTEGER, PARAMETER :: ppm_kind_double = KIND(1.0D0)
REAL(ppm_kind_double) :: before,after,dx
before = -0.112
dx = 0.008
after = CEILING(before/dx)
WRITE(*,*) before, dx, before/dx, after
END
And I got results:
The value I give to 'before' and 'dx' in the code is just for demonstration. For those before/dx = -13.5 for example, I want to use CEILING to get -13. But for the picture I show, I actually want to get -14. I have considered using some arguments like
IF(ABS(NINT(before/dx) - before/dx) < 0.001)
But that's simply not beautiful. Is there any better way to do this?
Update:
I was surprised to find that the problem won't occur if I set the variables to constants in ppm_kind_double. So I guess this 'rounding error' will only happen when the number of digits for rounding accuracy of the machine I use is more than what's defined in ppm_kind_double. I actually run my program(not this demo code) on a cluster, which I don't know about the machine precision. So maybe it's quad precision on that machine that leads to the problem?
After I set constants to double precision:
before = -0.112_ppm_kind_double
dx = 0.008_ppm_kind_double

This is a bit tricky, because you never know where the rounding error comes from. If dx was just a tiny bit larger than 0.008 then the division before/dx might still be rounded to the same value, but now -13 would be the correct answer.
That said, the most common method around that that I have seen is to just nudge the previous value ever so little into the opposite direction. Something like this:
program sign_test
use iso_fortran_env
implicit none
real(kind=real64) :: a, b
integer(kind=int32) :: c
a = -0.112
b = 0.008
c = my_ceiling(a/b)
print*, a, b, c
contains
function my_ceiling(v)
implicit none
real(kind=real64), intent(in) :: v
integer(kind=int32) :: my_ceiling
my_ceiling = ceiling(v - 1d-6, kind=int32)
end function my_ceiling
end program sign_test
This won't have any impact on the vast majority of values, but there are now a few values that will get rounded up by more than intended.

note if your reals are notionally "exact" to a specified precision you might do something like this:
after=nint(1000*before)/nint(1000*dx)
this works for your example.. you haven't said what you'd expect for both values positive and so on so you might need to work it a bit.

Related

Solving linear equations on Fortran using LAPACK [duplicate]

I have this line in fortran and I'm getting the compiler error in the title. dFeV is a 1d array of reals.
dFeV(x)=R1*5**(15) * (a**2) * EXP(-(VmigFe)/kbt)
for the record, the variable names are inherited and not my fault. I think this is an issue with not having the memory space to compute the value on the right before I store it on the left as a real (which would have enough room), but I don't know how to allocate more space for that computation.
The problem arises as one part of your computation is done using integer arithmetic of type integer(4).
That type has an upper limit of 2^31-1 = 2147483647 whereas your intermediate result 5^15 = 30517578125 is slightly larger (thanks to #evets comment).
As pointed out in your question: you save the result in a real variable.
Therefor, you could just compute that exponentiation using real data types: 5.0**15.
Your formula will end up like the following
dFeV(x)= R1 * (5.0**15) * (a**2) * exp(-(VmigFe)/kbt)
Note that integer(4) need not be the same implementation for every processor (thanks #IanBush).
Which just means that for some specific machines the upper limit might be different from 2^31-1 = 2147483647.
As indicated in the comment, the value of 5**15 exceeds the range of 4-byte signed integers, which are the typical default integer type. So you need to instruct the compiler to use a larger type for these constants. This program example shows one method. The ISO_FORTRAN_ENV module provides the int64 type. UPDATE: corrected to what I meant, as pointed out in comments.
program test_program
use ISO_FORTRAN_ENV
implicit none
integer (int64) :: i
i = 5_int64 **15_int64
write (*, *) i
end program
Although there does seem to be an additional point here that may be specific to gfortran:
integer(kind = 8) :: result
result = 5**15
print *, result
gives: Error: Result of exponentiation at (1) exceeds the range of INTEGER(4)
while
integer(kind = 8) :: result
result = 5**7 * 5**8
print *, result
gives: 30517578125
i.e. the exponentiation function seems to have an integer(4) limit even if the variable to which the answer is being assigned has a larger capacity.

Difference between variable types for the same computation in Fortran

I am new to Fortran and I was experimenting with int and double variables. I saw that
when you divide for example
integer:: a = 5
integer:: b = 2
the outcome is 2
However I was wondering when we use different types is there a difference of speed? Are they calculated the same way?
For example
double :: a = 2.0
integer :: b = 2
1) a**b
2) a**a
3) b**a
Of course the outcome for all these will be the same since they turn to double. However are they calculated the same way? Is there a difference in the speed they calculated?
EDIT : I must admit I did not know that the compiler plays a role. So far I know about 3 compilers : gfortran, nagfor and ifort. Personally I have experience in just gfortran and I tried and I got the same results in all the 3 calculations. However are they calculated the same way?
Normally, when optimizations are enabled, a**2 with a literal 2 will be changed to a*a. It is less likely, but not impossible, for the compiler to do such a thing for a variable integer exponent.
A completely generic exponentiation to a real exponent is implemented using logarithms. You just need the exp(x) function and then you can exponentiate any other number to the power of x if you know the logarithm of your number.
You can test the gfortran optimizations online https://godbolt.org/z/MvGEnn
You get a call to __powidf2() in the first case, and calls to pow() in the other cases.
Those are functions from the C runtime library.
double __powidf2 (double a, int b)
https://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html
double pow(double x, double y);
https://linux.die.net/man/3/pow
The former one is a specialized function to exponentiate to an integer and is much faster, the other is for two doubles.
You can play with the optimization level and you can also make on of the numbers known.
Like this one, where the optimizer can treat it as a constant even when it is a variable:
https://godbolt.org/z/YT3KP8
However, the compiler will not do that, if the value is only known outside the subroutine.
But when you use -fwhole-program, the compiler is actually able the pre-compute the result from the subroutine https://godbolt.org/z/zs43jv
I hope it illustrates that the problem is actually quite complex and cannot be answered in all generality.

Fortran 90 GDB signal SIGFPE, Arithmetic exception

I am trying to debug with GDB, and am encountering an arithmetic error when trying to compare two values.
The first value is set at the top of the module as double precision, parameter, public :: Dint = -1.D99
The second value happens to be inta = 102 inside the subroutine being called.
The comparison happens within a subroutine as If (Inta /= int(Dint)) then
I've tried looking at the value for Dint by typing p Dint, but it says it is not in current context. I suspect it may have been optimized out, or perhaps there is a certain syntax needed to see variables at the top of a module.
I also suspect maybe there could be an issue in attempting to convert this double precision to an integer, but I think it is working in other cases.
Any insight might be helpful.

Fast round up/down double in fortran?

Is there fast way to do round up/down in Fortran?
Because of linear order of bit-representation of positive double numbers it's possible to implement rounding as below.
pinf and ninf are global constants which are +/- infinity respectively
function roundup(x)
double precision ,intent(in) :: x
double precision :: roundup
if (isnan(x))then
roundup = pinf
return
end if
if (x==pinf)then
roundup = pinf
return
end if
if (x==ninf)then
roundup = ninf
return
end if
if (x>0)then
roundup = transfer((transfer(x,1_8)+1_8),1d0)
else if (x<0) then
roundup = transfer((transfer(x,1_8)-1_8),1d0)
else
if (transfer(x,1_8)==Z'0000000000000000')then
roundup = transfer((transfer(x,1_8)+1_8),1d0)
else
roundup = transfer((transfer(-x,1_8)+1_8),1d0)
end if
end if
end function roundup
I feel it's not the best way to do that because it's slow, but it uses almost only bit-operations.
Another way is using multiplication and some epsilon
eps = epsilon (1d0)
function roundup2(x)
double precision ,intent(in) :: x
double precision :: roundup2
if (isnan(x)) then
roundup2 = pinf
return
else if (x>=eps) then
roundup2 = x*(1d0+eps)
else if (x<=-eps) then
roundup2 = x*(1d0-eps)
else
roundup2 = eps
end if
end function roundup2
For some x both functions returns the same result (1d0, 158d0), for some don't (0.1d0, 15d0).
The first function is more accurate, but it's about 3.6 times slower than second
(11.1 vs 3.0 seconds on 10^9 rounds test)
print * ,x,y,abs(x-y)
do i = 1, 1000000000
x = roundup(x)
!y = roundup2(y)
end do
print * ,x,y,abs(x-y)
With no checks for NaN/Infinities first function test takes 8.5 seconds (-20%).
I use round function really hard and it takes a lot of time in profile of program. Is there cross-platform way to round faster with no loose of precision?
Update
The question suspects calls of roundup and rounddown at the time with no ability to reorder them. I didn't mention rounddown to keep topic short.
Hint:
First function uses two transfer function and one adding. And it's slower than one multiplication and one adding in the second case. Why transfer cost so much when it doesn't do any with the number's bits? Is it possible to replace transfer by faster function(s) or avoid addition calls at all?
I would recommend that you look at the Fortran standard IEEE floating point intrinsic modules (IEEE_ARITHMETIC, IEEE_FEATURES, IEEE_EXCEPTIONS). These provide IEEE_SET_ROUNDING_MODE where you can set the rounding mode for subsequent operations. Ideally you'd use IEEE_GET_ROUNDING_MODE to get the current mode and save it, set the new one, do your operations, then restore the mode.
Some caveats - changing the processor rounding mode is itself a slow operation, but if you do it once and then do lots of rounds, that will be a win. Not all current Fortran compilers support the IEEE intrinsic modules, but most reasonable ones should. You might need to tell the compiler you are playing with the IEEE environment - for Intel Fortran, use "-fp-model strict".
If I'm understanding correctly what you want to do, doesn't the "nearest" intrinsic do what you want, if you feed it +/- infinity as the arguments?
http://gcc.gnu.org/onlinedocs/gfortran/NEAREST.html#NEAREST
This might work, if the compiler implements this with decent performance. If you want NaN to round to Inf, you'll have to add that in a wrapper.
As for why roundup2 is faster, I can't tell for certain what's going on on your machine, but I can say two things:
The addition in roundup2 is probably optimized out (if eps is a parameter?) , so there's really just a multiplication.
If the transfer really does anything at all, that could easily slow the function down noticeably, since the function itself is so short. That might even be true if the transfer is just making superfluous copies of x.

FFTW: Trouble with real to complex and complex to real 2D tranfsorms

As the title states I'm using FFTW (version 3.2.2) with Fortran 90/95 to perform a 2D FFT of real data (actually a field of random numbers). I think the forward step is working (at least I am getting some ouput). However I wanted to check everything by doing the IFFT to see if I can re-construct the original input. Unfortunately when I call the complex to real routine, nothing happens and I obtain no error output, so I'm a bit confused. Here are some code snippets:
implicit none
include "fftw3.f"
! - im=501, jm=401, and lm=60
real*8 :: u(im,jm,lm),recov(im,jm,lm)
complex*8 :: cu(1+im/2,jm)
integer*8 :: planf,planb
real*8 :: dv
! - Generate array of random numbers
dv=4.0
call random_number(u)
u=u*dv
recov=0.0
k=30
! - Forward step (FFT)
call dfftw_plan_dft_r2c_2d(planf,im,jm,u(:,:,k),cu,FFTW_ESTIMATE)
call dfftw_execute_dft_r2c(planf,u(:,:,k),cu)
call dfftw_destroy_plan(planf)
! - Backward step (IFFT)
call dfftw_plan_dft_c2r_2d(planb,im,jm,cu,recov(:,:,k),FFTW_ESTIMATE)
call dfftw_execute_dft_c2r(planb,cu,recov(:,:,k))
call dfftw_destroy_plan(planb)
The above forward step seems to work (r2c) but the backward step does not seem to work. I checked this by differencing the u and recov arrays - which ended up not being zero. Additionally the max and min values of the recov array were both zero, which seems to indicate that nothing was changed.
I've looked around the FFTW documentation and based my implementation on the following page http://www.fftw.org/fftw3_doc/Fortran-Examples.html#Fortran-Examples . I am wondering if the problem is related to indexing, at least that's the direction I am leaning. Anyway, if any one could offer some help, that would be wonderful!
Thanks!
Not sure if this is the root of all troubles here, but the way you declare variables may be the culprit.
For most compilers (this is apparently not even a standard), Complex*8 is an old syntax for single precision: the complex variable occupies a total of 8 bytes, shared between the real and the imaginary part (4+4 bytes).
[Edit 1 following Vladimir F comment to my answer, see his link for details:] In my experience (i.e. the systems/compiler I ever used), Complex(Kind=8) corresponds to the declaration of a double precision complex number (a real and an imaginary part, both of which occupy 8 bytes).
On any system/compiler, Complex(Kind=Kind(0.d0)) should declare a double precision complex.
In short, your complex array does not have the right size. Replace occurences of Real*8 and Complex*8 by Real(kind=8) and Complex(Kind=8) (or Complex(Kind=kind(0.d0)) for a better portability), respectively.