|Fortran Programming Guide|
This chapter discusses the porting of programs from other dialects of Fortran to Sun compilers. VAX VMS Fortran programs compile almost exactly as is with Sun
f77; this is discussed further in the chapter on VMS extensions in the FORTRAN 77 Language Reference Manual.
Note Porting issues bear mostly upon Fortran 77 programs. The Sun WorkShop Fortran 95 compiler,
f95, incorporates few nonstandard extensions, and these are described in the Fortran User's Guide.
Time and Date Functions
Library functions that return the time of day or elapsed CPU time vary from system to system.
The following time functions are not supported directly in the Sun Fortran libraries, but you can write subroutines to duplicate their functions:
The time functions supported in the Sun Fortran library are listed in the following table:
TABLE 7-1 Fortran Time Functions
Returns the number of seconds elapsed since January, 1, 1970
Returns date as a character string
Returns the current time and date as a character string
Returns the current month, day, and year in an integer array
Returns the current hour, minute, and second in an integer array
Converts the time returned by the
timefunction to a character string
Converts the time returned by the
timefunction to the local time
Converts the time returned by the
timefunction to Greenwich time
Single processor: Returns elapsed user and system time for program execution
Multiple processors: Returns the wall clock time
Returns the elapsed user and system time since last call to
Returns date and time in character and numeric form
For details, see Fortran Library Reference Manual or the individual man pages for these functions.
The routines listed in the following table provide compatibility with VMS Fortran system routines
time. To use these routines, you must include the
-lV77option on the
f77command line, in which case you also get these VMS versions instead of the standard
date(3F) routine and the VMS version of
idate(3F) cannot be Year 2000 safe because they return 2-digit values for the year. Programs that compute time duration by subtracting dates returned by these routines will compute erroneous results after December 31, 1999. The Fortran 95 routine
date_and_time(3F) is available for both FORTRAN 77 and Fortran 95 programs, and should be used instead. See the Fortran Library Reference Manual for details.
The error condition subroutine
errsnsis not provided, because it is totally specific to the VMS operating system.
Here is a simple example of the use of these time functions (
subroutine startclockcommon / myclock / mytimeinteger mytime, timemytime = time()returnendfunction wallclockinteger wallclockcommon / myclock / mytimeinteger mytime, time, newtimenewtime = time()wallclock = newtime - mytimemytime = newtimereturnendinteger wallclock, elapsedcharacter*24 greetingreal dtime, timediff, timearray(2)c print a headingcall fdate( greeting )print*, " Hello, Time Now Is: ", greetingprint*, "See how long 'sleep 4' takes, in seconds"call startclockcall system( 'sleep 4' )elapsed = wallclock()print*, "Elapsed time for sleep 4 was: ", elapsed," seconds"c now test the cpu time for some trivial computingtimediff = dtime( timearray )q = 0.01do 30 i = 1, 1000q = atan( q )30 continuetimediff = dtime( timearray )print*, "atan(q) 1000 times took: ", timediff ," seconds"end
Running this program produces the following results:
TimeTestHello, Time Now Is: Mon Feb 12 11:53:54 1996See how long 'sleep 4' takes, in secondsElapsed time for sleep 4 was: 5 secondsatan(q) 1000 times took: 2.26550E-03 secondsdemo%
f95format edit descriptors can behave differently on other systems. Here are some format specifiers that
f77treats differently than some other implementations:
A- Alphanumeric conversion. Used with character type data elements. In FORTRAN 77, this specifier worked with any variable type.
f77supports the older usage, up to four characters to a word.
$- Suppresses newline character output.
R- Sets an arbitrary radix for the
Iformats that follow in the descriptor.
SU- Selects unsigned output for following
Iformats. For example, you can convert output to either hexadecimal or octal with the following formats, instead of using the
10 FORMAT( SU, 16R, I4 )20 FORMAT( SU, 8R, I4 )
Fortran carriage-control grew out of the capabilities of the equipment used when Fortran was originally developed. For similar historical reasons, an operating system derived from the UNIX operating system, does not have Fortran carriage control, but you can simulate it in two ways.
- Use the
asafilter to transform Fortran carriage-control conventions into the UNIX carriage-control format (see the
asa(1) man page) before printing files with the
f77:For simple jobs, use
OPEN(N, FORM='PRINT')to enable single or double spacing, formfeed, and stripping off of column one. It is legal to reopen unit 6 to change the form parameter to
OPEN( 6, FORM='PRINT')
Working With Files
Early Fortran systems did not use named files, but did provide a command line mechanism to equate actual file names with internal unit numbers. This facility can be emulated in a number of ways, including standard UNIX redirection.
The data file9 9.9
The source file
read(*,*) i, zThe program reads standard inputprint *, i, zstopend
f77 -silent -o redir redir.fThe compilation step
redir < redir.data
Run with redirection reads data file9 9.90000demo%
Porting From Scientific Mainframes
If the application code was originally developed for 64-bit (or 60-bit) mainframes such as CRAY or CDC, you might want to compile these codes with the following
options when porting to an UltraSPARC-II platform, for example:
-fast -xarch=v9a -xchip=ultra2 \ -xtypemap=real:64,double:64,integer:64
These options automatically promote all default
REALvariables and constants to
COMPLEX*16. Only undeclared variables or variables declared as simply
COMPLEXare promoted; variables declared explicitly (for example,
REAL*4) are not promoted. All single-precision
REALconstants are also promoted to
-xchipappropriately for the target platform.) To also promote default
REAL*16, change the
-xtypemapoption, is preferred over
-i2. See the Fortran User's Guide and the
f95(1) man pages for details.
To further recreate the original mainframe environment, it is probably preferable to stop on overflows, division by zero, and invalid operations. Compile the main program with
-ftrap=commonto ensure this.
The FORTRAN 77 Language Reference Manual, Fortran User's Guide, and the Sun Numerical Computation Guide discuss in detail the hardware representation of data objects in Fortran. Differences between data representations across systems and hardware platforms usually generate the most significant portability problems.
The following issues should be noted:
- Sun adheres to the IEEE Standard 754 for floating-point arithmetic. Therefore, the first four bytes in a
REAL*8are not the same as in a
- The default sizes for reals, integers, and logicals are described in the FORTRAN 77 standard, except when these default sizes are changed by the
-xtypemap=option (or by
- Character variables can be freely mixed and equivalenced to variables of other types, but be careful of potential alignment problems.
f77IEEE floating-point arithmetic does raise exceptions on overflow or divide by zero but does not signal
SIGFPEor trap by default. It does deliver IEEE indeterminate forms in cases where exceptions would otherwise be signaled. This is explained in the Floating Point Arithmetic chapter of this Guide.
- The extreme finite, normalized values can be determined. See
libm_double(3F). The indeterminate forms can be written and read, using formatted and list-directed I/O statements.
Many "dusty-deck" Fortran applications store Hollerith ASCII data into numerical data objects. With the 1977 Fortran standard (and Fortran 95), the
CHARACTERdata type was provided for this purpose and its use is recommended. You can still initialize variables with the older Fortran Hollerith (n
H) feature, but this is not standard practice. The following table indicates the maximum number of characters that will fit into certain data types. (In this table, boldfaced data types indicate default types subject to promotion by the
f77:Maximum Characters in Data Types
BYTE 1 1 1 1 1
8 8 8 16 16 COMPLEX*16 16 16 16 16 16 COMPLEX*32 32 32 32 32 32
16 16 16 32 32
8 8 8 16 16
4 2 4 4 8 INTEGER*2 2 2 2 2 2 INTEGER*4 4 4 4 4 4 INTEGER*8 8 8 8 8 8
4 2 4 4 8 LOGICAL*1 1 1 1 1 1 LOGICAL*2 2 2 2 2 2 LOGICAL*4 4 4 4 4 4 LOGICAL*8 8 8 8 8 8
4 4 4 8 8 REAL*4 4 4 4 4 4 REAL*8 8 8 8 8 8 REAL*16 16 16 16 16 16
When storing standard ASCII characters with normal Fortran:
-r8, unspecified size
LOGICALdo not hold double.
-dbl, unspecified size
LOGICALdo hold double.
The storage is allocated with both options, but it is unavailable in normal Fortran with
-dblare now considered obsolete; use
Example: Initialize variables with Hollerith:
cat FourA8.fdouble complex x(2)data x /16Habcdefghijklmnop, 16Hqrstuvwxyz012345/write( 6, '(4A8, "!")' ) xenddemo%
f77 -silent -o FourA8 FourA8.fdemo%
If you pass Hollerith constants as arguments, or if you use them in expressions or comparisons, they are interpreted as character-type expressions.
If needed, you can initialize a data item of a compatible type with a Hollerith and then pass it to other routines.
program respondinteger yes, nointeger askdata yes, no / 3hyes, 2hno /if ( ask() .eq. yes ) thenprint *, 'You may proceed!'elseprint *, 'Request Rejected!'endifendinteger function ask()double precision solaris, responseinteger yes, nodata yes, no / 3hyes, 2hno /data solaris/ 7hSOLARIS/10 format( "What system? ", $ )20 format( a8 )write( 6, 10 )read ( 5, 20 ) responseask = noif ( response .eq. solaris ) ask = yesreturnend
Nonstandard Coding Practices
As a general rule, porting an application program from one system and compiler to another can be made easier by eliminating any nonstandard coding. Optimizations or work-arounds that were successful on one system might only obscure and confuse compilers on other systems. In particular, optimized hand-tuning for one particular architecture can cause degradations in performance elsewhere. This is discussed later in the chapters on performance and tuning. However, the following issues are worth considering with regards to porting in general.
Some systems automatically initialize local and COMMON variables to zero or some "not-a-number" (NaN) value. However, there is no standard practice, and programs should not make assumptions regarding the initial value of any variable. To assure maximum portability, a program should initialize all variables.
Aliasing Across Calls
Aliasing occurs when the same storage address is referenced by more than one name. This happens when actual arguments to a subprogram overlap between themselves or between COMMON variables within the subprogram. For example, arguments X and Z refer to the same storage locations, as do B and H:
COMMON /INS/B(100)REAL S(100), T(100)...CALL SUB(S,T,S,B,100)...SUBROUTINE SUB(X,Y,Z,H,N)REAL X(N),Y(N),Z(N),H(N)COMMON /INS/B(100)...
Avoid aliasing in this manner in all portable code. The results on some systems and with higher optimization levels could be unpredictable.
Legacy codes may contain source-code restructurings of ordinary computational DO loops intended to cause older vectorizing compilers to generate optimal code for a particular architecture. In most cases, these restructurings are no longer needed and may degrade the portability of a program. Two common restructurings are strip-mining and loop unrolling.
Fixed-length vector registers on some architectures led programmers to manually "strip-mine" the array computations in a loop into segments:
REAL TX(0:63)...DO IOUTER = 1,NX,64DO IINNER = 0,63TX(IINNER) = AX(IOUTER+IINNER) * BX(IOUTER+IINNER)/2.QX(IOUTER+IINNER) = TX(IINNER)**2END DOEND DO
Unrolling loops by hand was a typical source-code optimization technique before compilers were available that could perform this restructuring automatically. A loop written as:
DO K = 1, N-5, 6DO J = 1, NDO I = 1,NA(I,J) = A(I,J) + B(I,K ) * C(K ,J)* + B(I,K+1) * C(K+1,J)* + B(I,K+2) * C(K+2,J)* + B(I,K+3) * C(K+3,J)* + B(I,K+4) * C(K+4,J)* + B(I,K+5) * C(K+5,J)END DOEND DOEND DODO KK = K,NDO J =1,NDO I =1,NA(I,J) = A(I,J) + B(I,KK) * C(KK,J)END DOEND DOEND DO
Here are a few suggestions for what to try when programs ported to Sun Fortran do not run as expected.
Results Are Close, but Not Close Enough
Try the following:
- Pay attention to the size and the engineering units. Numbers very close to zero can appear to be different, but the difference is not significant, especially if this number is the difference between two large numbers, such as the distance across the continent in feet, as calculated on two different computers. For example, 1.9999999e-30 is very near -9.9992112e-33, even though they differ in sign.
- VAX math is not as good as IEEE math, and even different IEEE processors may differ. This is especially true if the mathematics involves many trigonometric functions. These functions are much more complicated than one might think, and the standard defines only the basic arithmetic functions. There can be subtle differences, even between IEEE machines. Review the Floating-Point Arithmetic chapter in this Guide.
- Try running with a
call nonstandard_arithmetic(). Doing so can also improve performance considerably, and make your Sun workstation behave more like a VAX system. If you have access to a VAX or some other system, run it there also. It is quite common for many numerical applications to produce slightly different results on each floating-point implementation.
- Check for NaN, +Inf, and other signs of probable errors. See the Floating-Point Arithmetic chapter in this Guide, or the man page
ieee_handler(3m) for instructions on how to trap the various exceptions. On most machines, these exceptions simply abort the run.
- Two numbers can differ by 6 x 1029 and still have the same floating-point form. Here is an example of different numbers, with the same representation:
real*4 x,yx=99999990e+29y=99999996e+29write (*,10) x, x10 format('99,999,990 x 10^29 = ', e14.8, ' = ', z8)write(*,20) y, y20 format('99,999,996 x 10^29 = ', e14.8, ' = ', z8)end
- The output is:
99,999,990 x 10^29 = 0.99999993E+37 = 7CF0BDC199,999,996 x 10^29 = 0.99999993E+37 = 7CF0BDC1
- In this example, the difference is 6 x 1029. The reason for this indistinguishable, wide gap is that in IEEE single-precision arithmetic, you are guaranteed only six decimal digits for any one decimal-to-binary conversion. You may be able to convert seven or eight digits correctly, but it depends on the number.
Program Fails Without Warning
If the program fails without warning and runs different lengths of time between failures, then:
- Compile with minimal optimization (
-O1). If the program then works, compile only selective routines with higher optimization levels.
- Understand that optimizers must make assumptions about the program. Nonstandard coding or constructs can cause problems. Almost no optimizer handles all programs at all levels of optimization.
Sun Microsystems, Inc.
Copyright information. All rights reserved.
|Library | Contents | Previous | Next | Index|