Re: Desirable Usage of Fortran Modules
From: James Van Buskirk (not_valid_at_comcast.net)
Date: 03/12/04
- Previous message: glen herrmannsfeldt: "Re: first time using direct access files: recl ??"
- In reply to: Ed Wells: "Re: Desirable Usage of Fortran Modules"
- Next in thread: Walter Spector: "Re: Desirable Usage of Fortran Modules"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 12 Mar 2004 20:14:51 GMT
"Ed Wells" <wellsed@wam.umd.edu> wrote in message
news:692b4a0d.0403112206.199c2ca7@posting.google.com...
> James, I've been admiring your signature, but I cannot for the life of
> me understand how TRANSFER works. Could you please enlighten me?
The usual place to look turns up some documentation:
http://h18009.www1.hp.com/fortran/docs/lrm/lrm0344.htm#transfer_intrin
We see that TRANSFER takes three arguments: SOURCE, MOLD, and the
optional argument SIZE. The SOURCE argument is interpreted as
an ordered bucket of bits and the MOLD argument tells the compiler
how to interpret the bit-bucket.
First example: suppose we want to cast a character array as
a string. We can do this several ways:
1) A couple of levels of subroutine call
2) A function that returns a string
3) TRANSFER!!
module convert
implicit none
contains
subroutine cast_sub1(x)
character x(:)
call cast_sub2(x,size(x))
end subroutine cast_sub1
subroutine cast_sub2(x,n)
integer, intent(in) :: n
character x(n)
call cast_sub3(x(1),n)
end subroutine cast_sub2
subroutine cast_sub3(x,n)
integer, intent(in) :: n
character(n) x(1)
write(*,'(1x,a)') x
end subroutine cast_sub3
function cast_fun(x)
character, intent(in) :: x(:)
character(size(x)) cast_fun
integer i
do i = 1, size(x)
cast_fun(i:i) = x(i)
end do
end function cast_fun
end module convert
program transfer_ex
use convert
implicit none
character mess(8)
mess = (/'M','e','s','s','a','g','e','!'/) ! Yuck, looks like C
write(*,'(a)') ' This is what it looks like in a ''(1x,a)'' format.'
write(*,'(1x,a)') mess
write(*,'(a)') ' Now the subroutine version:'
call cast_sub1(mess)
write(*,'(a)') ' Now the function version:'
write(*,'(1x,a)') cast_fun(mess)
write(*,'(a)') ' Now for TRANSFER!'
write(*,'(1x,a)') transfer(mess,repeat('x',size(mess)))
end program transfer_ex
Output with LF95 5.70d:
This is what it looks like in a '(1x,a)' format.
M
e
s
s
a
g
e
!
Now the subroutine version:
Message!
Now the function version:
Message!
Now for TRANSFER!
Message!
In this case the action of TRANSFER is fairly transparent:
we want to cast the mess array of character*1 elements
mess = (/'M','e','s','s','a','g','e','!'/)
as a single character*8 variable. Sometimes it's easier
to think of a string a an array of character*1 because we
can apply Fortran's powerful array transformational
intrinsics to it and sometimes it's better to think of
the string as a single long scalar character variable
because we can then apply Fortran's string manipulation
functions to it. We could get a character*8 version of
our message by transferring from the original array to
a character*8 scalar. The MOLD argument of
repeat('x',size(mess)) is a character expression as long
as mess has array elements. You can transfer from a
long scalar character expression to a character*1 array
by making the MOLD argument a character*1 array:
transfer('Message!',(/'x'/))
would be an array equal to what the mess array in our
example above; that is:
all(mess == transfer('Message!',(/'x'/)))
would print as .TRUE.
It's simple to envision the action of TRANSFER when both
input and output are CHARACTER type variables, but life
is much more interesting and non-transportable when
variables of dissimilar types are involved. Here, I
have a program that converts an input string to an
array of double precision numbers:
program makesig
implicit none
type charnode
character x
type(charnode), pointer :: next => NULL()
end type charnode
type(charnode), pointer :: head => NULL()
type(charnode), pointer :: temp
integer :: numchar = 0
integer iostat
character x
integer, parameter :: dp = selected_real_kind(15,300)
integer, parameter :: qp = selected_real_kind(30,1000)
write(*,'(a)',advance='no') ' Enter the string:> '
do
read(*,'(a)',advance = 'no',iostat=iostat) x
if(iostat /= 0) exit
numchar = numchar+1
allocate(temp)
temp%x = x
temp%next => head
head => temp
end do
call step_2(head, numchar)
contains
subroutine step_2(head, numchar)
type(charnode), pointer :: head
integer, intent(in) :: numchar
character(numchar) string
integer, parameter :: nchars = size(transfer(1.0_dp,(/'x'/)))
character(nchars*((numchar+nchars-1)/nchars)) full_str
real(dp) full_num(len(full_str)/nchars)
type(charnode), pointer :: temp
integer i
character(80) fmt
integer, parameter :: ndigits = precision(1.0_dp)
real(qp) quad
! real(dp) quad
do i = numchar, 1, -1
temp => head
head => head%next
nullify(temp%next)
string(i:i) = temp%x
deallocate(temp)
end do
full_str = string
write(*,*) '#'//full_str//'#'
full_num = transfer(full_str,full_num)
write(fmt,'(a,i0,a,i0,a)') '(1x,d',ndigits+9,'.',ndigits+2,')'
do i = 1, size(full_num)
quad = full_num(i)
write(*,fmt) quad-spacing(full_num(i))
write(*,fmt) quad
write(*,fmt) quad+spacing(full_num(i))
write(*,'()')
end do
end subroutine step_2
end program makesig
Now, this program is kind of awkward due to compiler bugs:
I don't always like the way LF95 prints out extra digits
of a double precision variable and the latest version of
CVF (6.6C3) has a bug that breaks on this code. Neither
problem is due to TRANSFER, Richard! If you do manage to
get it to work, however, the interesting part happens in
subroutine step_2 after the line
full_str = string
where the user's input string has finally been converted
to a character variable of length equal to the size of his
input padded to the next highest multiple of a double
precision variable's length in characters. The next line
enables you to see the input with padding. Here's a
sample run:
Enter the string:> Secret message
#Secret message #
0.45379127063540210+218
0.45379127063540220+218
0.45379127063540230+218
0.61143882726677190-153
0.61143882726677200-153
0.61143882726677220-153
So how did it get these two numbers? First off, the
input string had 14 characters or 14 bytes of ASCII
and an IEEE-754 64-bit double precision variable
occupies 8 bytes of memory, so we must round up to
16 bytes by padding with two spaces. Having done
this, let's look at what we have as a hex dump:
S e c r e t m e s s a g e
53 65 63 72 65 74 20 6d 65 73 73 61 67 65 20 20
Since all my machines are little-endian, our array
as hex numbers will be:
6d20746572636553 2020656761737365
For the first number, the high (sign) bit is zero, so
the number is positive. Pull of the next 11 bits:
B'11011010010' = 1746
Subtract 1023 to get 1746-1023 = 723
Now take the last 52 bits Z'0746572636553' = 127979059635539
Divide by 2**52 to get 127979059635539/2**52 =
0.028417059735450278523671840957832
Add the implied one bit and multiply by 2**723 to get
2**723*1.028417059735450278523671840957832 =
4.537912706354022390361624462237e+217, quite close to
the middle of the upper set of three numbers.
The next number is also positive. B'01000000010' = 514
514-1023 = -509
Z'0656761737365' = 111494690992997
111494690992997/2**52 = 0.024756794612778465491942370135803
(1+0.024756794612778465491942370135803)*2**(-509) =
6.11438827266772017821835579061e-154
Good thing Fortran can do all of this so easily for us
via TRANSFER, right? OK, now we can write a Fortran
program that takes these double precision constants and
decodes them back into ASCII:
write(*,*) transfer((/0.4537912706354022d+218, &
0.6114388272667720d-153/),(/'x'/));end
We see in the above that SOURCE is an array of double
precision literals that contains our encoded data in
order. We had to add the 'd' exponents ourselves because
the 'd' format descriptor can't do that. Then we make
MOLD an array of character*1 literals so that the output
will be an array of 16 characters that has the same
bit pattern in order as the encoded data. Output:
Secret message
Hope this helps in your understanding of the most
interesting (to me) low-level programming feature
among the features new to F90. The spammers seem
to experience a certain level of difficulty in
figuring this out, but the Enlightened Reader
either has a Fortran 95 compiler or can puzzle out
the low-level details outlined above for himself.
-- write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, & 6.0134700243160014d-154/),(/'x'/)); end
- Previous message: glen herrmannsfeldt: "Re: first time using direct access files: recl ??"
- In reply to: Ed Wells: "Re: Desirable Usage of Fortran Modules"
- Next in thread: Walter Spector: "Re: Desirable Usage of Fortran Modules"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|