In fortran, can I always count on 0.d0 == 0 to result in .true. ?
I.e.:
real(8) :: x
integer :: i
x = 0.d0
i = 0
write(*,*) x == i
I have tried in a simple piece of code and it turns out as .true. but can I always trust this?
The reason for this is I want to avoid problems with acos(x) if x < -1.
i will be promoted to the floating point type before application of the relational equality test ==.
A floating point zero will compare true with itself, and also with a negative signed zero.
"Can I always trust this?" Yes, you can.
Related
I am a university lecturer, and I will teach the Numerical Methods course this semester using Fortran 90/95 as the programming language. The beginning of the course starts with the representation of numbers, and I would like to talk about the limits of numbers that can be represented with REAL(4), REAL(8) and REAL(16). I intend to use the following code on OnlineGDB (so that students won't have to install anything on their computers, which may be a pain in times of remote learning):
Program declare_reals
implicit none
real(kind = 4) :: a_huge, a_tiny ! single precision ; default if kind not specified
!real(4) :: a ! Equivalent to real(kind = 4) :: a
a_huge = huge(a_huge)
print*, "Max positive for real(4) : ", a_huge
a_tiny = tiny(a_tiny)
print*, "Min positive for real(4) : ", a_tiny
print*,
End Program declare_reals
With this code, I get
Max positive for real(4) : 3.40282347E+38
Min positive for real(4) : 1.17549435E-38
However, if I write a_tiny = tiny(a_tiny)/2.0, the output becomes
Min positive for real(4) : 5.87747175E-39
Looking at the documentation for gfortran (which OnlineGDB uses as the f95 compiler), I had the impression that anything below tiny(x) could result in an underflow and zero would show instead of a non-zero number. Could anyone help me understand what is happening here? If tiny(x) doesn't yield the smallest positive representable number, what is being shown due to the function call?
The Fortran Standard states the following about a real value:
The model set for real x is defined by
where b and p are integers exceeding one; each fk is
a non-negative integer less than b, with f1
nonzero; s is
+1 or −1; and e is an integer that lies between some integer maximum emax and some integer minimum emin inclusively. For x = 0, its exponent e and digits fk
are defined to be zero. The integer parameters b, p,
emin, and emax determine the set of model floating-point numbers.
Real values which satisfy this definition, are referenced to be model numbers or normal floating point numbers. The floating point numbers your system can represent, i.e. the machine-representable numbers are a superset of the model numbers. They can, but not necessarily must, include the values with f1 zero — also known as subnormal floating point numbers — and are there to fill the underflow gap around zero.
The Fortran functions tiny(x), huge(x), epsilon(x), spacing(x) are all defined for model numbers.
The value of tiny(x) is given by bemin − 1, which for a single-precision floating-point number (binary32) is given by 2−126 and is the smallest model (normal) number. When your system follows IEEE754, the machine representable numbers will also contain the subnormal numbers. The smallest subnormal positive number is given bytiny(x)*epsilon(x) which in binary32 is 2−126 × 2−23. This explains why you can divide tiny(x) by two, i.e. the transition from normal to subnormal.
# smallest normal number
0 00000001 000000000000000000000002 = 0080 000016 = 2−126 ≈ 1.1754943508 × 10−38
# smallest subnormal number
0 00000000 000000000000000000000012 = 0000 000116 = 2−126 × 2−23 ≈ 1.4012984643 × 10−45
Note: when you divide tiny(x)*epsilon(x) by two, gfortran returns an arithmetic underflow error.
Ref: values taken from Wikipedia: Single precision floating-point format
I have the following fortran code defined under. I am trying to change the length of the do loop if i change the value of n. When i try to compile i get the error:
‘a’ argument of ‘floor’ intrinsic at (1) must be REAL. But when i change q and w to be defined as real i get another error message. How can i fix this? q and w is clearly a integer when i use floor(...)
subroutine boundrycon(n,bc,u,v)
!input
integer :: n,bc
!output
real(8) :: u(n+2,n+2), v(n+2,n+2)
!lokale
integer :: j,i,w,q
n=30
q=floor(n/2)
w=(floor(n/2)+floor(n/6))
do j=q,w
u(q,j)=0.0;
v(q+1,j)=-v(q,j);
u(w,j)=0.0;
v(w+1,j)=-v(w,j);
end do
do i=q,w
v(i,q)=0.0;
u(i,q)=-u(i,q+1);
u(i,w+1)=-u(i,w);
v(i,w)=0;
end do
end subroutine boundrycon
Many people have already pointed this out in the comments to your question, but here it is again as an answer:
In Fortran, if you do a division of two integer values, the result is an integer value.
6/3 = 2
If the numerator is not evenly divisible by the denominator, then the remainder is dropped:
7/3 = 2
Let's look at your code:
q=floor(n/2)
It first evaluates n/2 which, since both n and 2 are integers, is such an integer division. As mentioned before, this result is an integer.
This integer is then passed as argument to floor. But floor expects a floating point variable (or, as Fortran calls it: REAL). Hence the error message:
"[The] argument of floor ... must be REAL."
So, the easiest way to get what you want is to just remove the floor altogether, since the integer division does exactly what you want:
q = n/2 ! Integer Division
If you need to make a floating point division, that is if you want two integer variables to divide into a real variable, you have to convert at least one of them to floating point before the division:
print *, 3/2 ! wrong, prints 1
print *, real(3)/2 ! right
print *, 3/2.0 ! right
print *, (3 * 1.0) / 2 ! right
print *, real(3/2) ! wrong, prints 1.0
The following is the code I have written to find the DFT of sine(x) over a period.
program fftw_test
implicit none
INTEGER FFTW_MEASURE
PARAMETER (FFTW_MEASURE=0)
INTEGER FFTW_ESTIMATE
PARAMETER (FFTW_ESTIMATE=64)
INTEGER FFTW_FORWARD
PARAMETER (FFTW_FORWARD=-1)
integer, parameter :: n = 8
integer :: i
double complex, dimension(0:n-1) :: input, output
double precision, parameter :: pi = 3.141592653, h = 2.0d0*pi/(n)
integer*8 :: plan
call dfftw_plan_dft_1d(plan, n, input, output, fftw_forward, fftw_measure)
do i = 0, n-1
input(i) = cmplx(sin(h*i), 0)
end do
call dfftw_execute_dft(plan, input, output)
output = output/n
output(0) = cmplx(0,0) ! setting oddball wavenumber to be 0
call dfftw_destroy_plan(plan)
do i = -n/2, n/2-1, 1
write(*, *) i, output(i+(n/2))
end do
end program
I am aware of the r2c (real to complex) function in the FFTW library. But I was advised to use the normal c2c function. So I defined the input function as a complex number with real part = sine(x) and complex part 0.
The DFT of sine(x) is supposed to be fk(-1) = cmplx(0, -0.5) and fk(1) = cmplx(0, 0.5) where fk(k) means the fourier coefficient of the k wavenumber
The output I received is as follows.
-4 ( 0.0000000000000000 , 0.0000000000000000 )
-3 ( 3.2001271327131496E-008,-0.49999998518472011 )
-2 ( -1.0927847071684482E-008, 1.4901161193847656E-008)
-1 ( -1.0145577183762535E-008, 1.4815279864022202E-008)
0 ( -1.0927847071684482E-008, 0.0000000000000000 )
1 ( -1.0145577183762535E-008, -1.4815279864022202E-008)
2 ( -1.0927847071684482E-008, -1.4901161193847656E-008)
3 ( 3.2001271327131496E-008, 0.49999998518472011 )
I am getting fk(-3) = cmplx(~0, -0.5) and fk(3) = cmplx(~0, 0.5). If I increase the grid size to 16, 32 or so I get -n/2 -1 and n/2 -1 wavenumbers with the required values instead of the -1 and 1 wavenumbers.
Does this have something to do with the way FFTW stores the output in the output array ? Or am I going wrong anywhere else ?
Also, I don't seem to be getting 'proper 0' where I should be. It is instead numbers of the order of 10^(-8) which I believe is the smallest my datatype double can hold. Is that something I should be worried about ?
Like #VladimirF already said, the ordering of the values is a bit different, than you might expect. The first half of the array holds the positive frequencies, the second half holds the negative frequencies in reverse order (see this link). And you might have to check the sign convention used by FFTW.
The problem with accuracy stems from your single precision value for pi and the use of cmplx which produces single precision complex numbers (use the keyword argument kind). In this case you could simply assign your real value to the complex variables. Applying these two changes yields a precision of ~1e-10. This can be improved by supplying a better approximation for pi (i.e. more than 10 digits).
E.g. the value pi = 3.141592653589793d0 yields results with accuracy of 1e-16.
What is the best way to concatenate two integers to an integer in Fortran?
integer a = 999
integer b = 1111
integer c should be 9991111
Thanks,
SM.
Here is an example code that does what you need. It writes integers into character strings, trims and concatenetes them, and then reads the result integer from concatenated character string:
integer :: a,b,c
character(len=99) :: char_a,char_b,char_c
a = 999
b = 1111
write(unit=char_a,fmt=*)a
write(unit=char_b,fmt=*)b
char_c = trim(adjustl(char_a))//trim(adjustl(char_b))
read(unit=char_c,fmt=*)c
print*,c
end
Edit: Note that this example is general for any integer lengths, assuming they fit into their respective kind (no integer overflow).
You can use the information of the order of the number:
integer :: a = 999
integer :: b = 1111
integer :: c
c = a * 10**(ceiling(log10(real(b)))) + b
write(*,*) c
Your best bet is to use internal files to convert your two integers to a character, and then convert this back to an integer.
There is no intrinsic procedure for converting a numeric value to a character/string representation. See this discusson on Fortran Wiki for more information (see the part headed "Note").
As an example, in your case you could use the following:
program test_conversion
implicit none
integer :: a=999
integer :: b=1111
integer :: c
character(len=7) :: temp
write(temp, '(i3.3, i4.4)') a, b ! You may need to change these format specifiers
read(temp, *) c
print*, c ! This prints 9991111
end program test_conversion
You will have to change the format string if you want different widths of the character representation of your integers.
I'm rewriting some code to make a program compile with the gfortran compiler as opposed to ifort compiler I usually use. The code follows:
_Subroutine SlideBits (WORD, BITS, ADDR)
Implicit None
Integer(4) WORD
Integer(4) BITS
Integer(4) ADDR
Integer(4) ADDR1
ADDR1 = 32 - ADDR
WORD = (WORD .And. (.Not.ISHFT(1,ADDR1))) .Or. ISHFT(BITS,ADDR1)
End_
When I compile the above code using the gfortran compiler, I recieve this error:
WORD = (WORD .And. (.Not.ISHFT(1,ADDR1))) .Or. ISHFT(BITS,ADDR1)
Error: Operand of .NOT. operator at (1) is INTEGER(4)
All three of the variables coming into the subroutine are integers. I've looked around a bit and the gfortran wiki states that the gfortran compiler should be able to handle logical statments being applied to integer values. Several other sites I've visited either quote from the gnu wiki or agree with it. This is the first time I've seen this error as the Intel Fortran compiler (ifort) I normally use compiles cleanly.
The comments/answers above "may .Not. be" the correct responses, depending on your ultimate objective.
The likely purpose of that "WORD = .." statement is .NOT. to arrive at a boolean/logical result, but rather to obtain a kind of integer enumerator.
To see this, first "ignore" the bit shifting (iShift() etc), and just look at something like IntR = Int1 .Or. Int2. This will produce a "proper" integer result. The value will depend on not only the values of the int's, but also on their declared "type" (e.g. Integer(1), Integer(2), etc)
That is, the resulting value of WORD will be a "proper" integer; something like "33504" .. or whatever, (likely) .NOT. a 0/1 or -1/0 or .True./.False. etc
If you replace = Int1 .Or. Int2 with = (Int1 /= 0) .Or. (Int2 /= 0) ... you will get an "integer logical" (i.e. 0/1 etc) and WILL NOT produce the
desired enumerator ... if that is what you are looking for.
The .Or. on two Int's is a kind of bit-wise addition that produces a new num based on how the bits align/word size etc.
e.g. 3 == 011, 2 = 010 ... so, 3 .Or. 2 ==> 011 = 3
e.g. 3 == 011, 5 = 101 ... so, 3 .Or. 5 ==> 111 = 7
e.g. 5 == 101, 5 = 101 ... so, 5 .Or. 5 ==> 101 = 5
... similarly the .And. provides a kind of multiplication.
This technique is sometimes used to create enumerators somewhat like the use of powers of two (1,2,4,8...) are used to assign a value. Then, any sum of those
values can be decomposed, for example, into its constituent elements. For instance, if a(1) = 2, and a(2) = 8, then the sum 10 can be decomposed to
show the selections were the 1st and 4th elements of (1,2,4,8,...) etc.
It may help conceptualise this by noting that bit-shifting is like multiplying by 2 (for left shift) and dividing by 2 (for right shift).
BTW, you don't need to restrict to Fortran for this. Whack it into a VBA function and see the result in your spreadsheet VBA does not
have bit shift intrinsics, but they are available ... in any case it will demonstrate the Int1 .Or. Int2 behaviour even without bit shifting, such as
Function TwoIntsOr(Int1 As Long, Int2 As Long) As Long
'
TwoIntsOr = Int1 Or Int2
'
End Function
-- .Or. in Fortran
Function TwoIntsOr(Int1, Int2)
Integer :: TwoInstOr
Integer, Intent(In) :: Int1, Int2
!
TwoIntsOr = Int1 .Or. Int2
!
End Function
).
It is not standard Fortran to apply logical/boolean operators to integer variables. If the goal is a boolean result, the ideal solution would be to convert the types to logical. If, as it appears from casual examination, the code is really doing bit-wise operations, then it would be better to use the IAND and IOR intrinsic functions.
gfortran is expecting booleans for the logical operators and the code is providing integers. Use comparisons with zero instead of logical operators.
WORD = ((WORD /= 0) .And. (ISHFT(1,ADDR1) == 0)) .Or. (ISHFT(BITS,ADDR1) /= 0)
gfortran and ifort use different representations for .true. and .false. values, so it's best to stick to booleans when that's what the code needs. In a conversion from ifort to gfortran I got bit by the former representing .true. as -1 and the latter using 1 for the same purpose, instead of the traditional (C-like) not 0.