Are Fortran FORMATs done during compile time or execution time? - fortran

Part of the FORTRAN Syntax is FORMAT for example:
WRITE(*,10)'this is a string', 'this is a second string', 'this is a third string'
10 FORMAT(1x,a10,1x,a10,1x,a20)
What I want to know is when the code is formatted, is it compile-time or execution-time? How could I test this? Checking the differences in assembly?

The format strings are typically interpreted at runtime. After all those edits it is not that typical after all, see Steve's answer. The answer describes gfortran which might even be less typical, but is important and widespread. Especially when the format string is a character variable (that can be anything), but also the FORMAT statement. You often get an error message for incorrect format only at runtime. For example:
WRITE(*,10) x
10 FORMAT(i0)
END
gives
> ./a.out
At line 8 of file format3.f90 (unit = 6, file = 'stdout')
Fortran runtime error: Expected INTEGER for item 1 in formatted transfer, got REAL
(i0)
^
In your case:
gfortran -O3 format3.f90 -fdump-tree-optimized
gives
dt_parm.0.common.filename = &"format3.f90"[1]{lb: 1 sz: 1};
dt_parm.0.common.line = 1;
dt_parm.0.format = &"(1x,a10,1x,a10,1x,a20)"[1]{lb: 1 sz: 1};
dt_parm.0.format_len = 22;
dt_parm.0.common.flags = 4096;
dt_parm.0.common.unit = 6;
_gfortran_st_write (&dt_parm.0);
_gfortran_transfer_character_write (&dt_parm.0, &"this is a string"[1]{lb: 1 sz: 1}, 16);
_gfortran_transfer_character_write (&dt_parm.0, &"this is a second string"[1]{lb: 1 sz: 1}, 23);
_gfortran_transfer_character_write (&dt_parm.0, &"this is a third string"[1]{lb: 1 sz: 1}, 22);
_gfortran_st_write_done (&dt_parm.0);
dt_parm.0 ={v} {CLOBBER};
The _gfortran_st_write (&dt_parm.0); sets the writing mode and that includes the format string.
You can notice that the format is stored in a character variable and could be interpreted at runtime if necessary, but it is not actually used in the code generated by the compiler. It is potentially used inside _gfortran_transfer_character_write that are part of the libgfortran runtime library.
The above is for gfortran. One can imagine a lot can be actually optimized away and compiled, but I am not aware that compilers do that.
If you are curious, the gfortran format interpretting code is in https://github.com/gcc-mirror/gcc/blob/trunk/libgfortran/io/format.c

The compilers I have worked on have always translated the format to an internal representation at compile time, other than formats stored in a character variable. The run-time system still has to have the ability to "compile" formats at run-time, but the compiler can check for errors. The actual formatting of the output always happens at run-time.

Related

A unique type of data conversion

in the following code
tt=5;
for(i=0;i<tt;i++)
{
int c,d,l;
scanf("%lld%lld%lld",&c,&d,&l);
printf("%d %d %d %d",c,d,l,tt);
}
in the first iteration, the value of 'tt' is changing to 0 automatically.
I know that i have declared c,d,l as int and taking input as long long so it is making c,d=0. But still, i m not able to understand how tt is becoming 0.
Small, but obligatory announcement. As it was said in comments, you face undefined behavior, so
don't be surprised by tt assigned to zero
don't be surprised by tt not assigned to zero after insignificant code changes (e.g. reordering initialization from "int i,tt;" to "int tt, i;" or vice versa)
don't be surprised by tt not assigned to zero after compiling with different flags or different compiler version or for different platform or for testing with different input
don't be surprised by anything. Any behavior is possible.
You can't expect this code to work one way or another, so don't ever use it in real program.
However, you seem to be OK with that, and the question is "what is actually happening with tt". IMHO this question is really great, it reveals passion to understand programming deeper, and it helps in digging into lower layer. So lets get started.
Possible explanation
I failed to reproduce behavior on VS2015, but situation is quite clear. Actual data aligning, variable sizes, endianness, stack growth direction and other details may differ on your PC, but the general idea should be the same.
Variables i, tt, c, d, l are local, so they are stored on stack. Lets assume, sizeof(int) is 4 and sizeof(long long) is 8 which is quite common. Then one of possible data alignments is shown on picture (addresses grow from left to right, each cell represents one byte):
When doing scanf, you pass address of c (blue arrow on next pict) for filling with data. But size of data is 8 bytes, so data of both c and tt are overwritten (blue cells on the pict). For little-endian representation, you always write zeroes to tt unless really big number is entered by user, while c actually gets valid data for small numbers.
However, valid data in c will be rewritten the same way during filling d, the same will happen to d while filling l. So only l will get nonzero value in described case. Easy test: enter large number for c, d, l and check if tt is still zero.
How to get precise answer
You can get all answers from assembly code. Enable disassembly listing (exact steps depend on toolchain: gcc has -S option, visual studio has "goto disassembly" item in context menu while on breakpoint) and analyze listing. It's really helpful to see exact instructions your CPU is going to execute. Some debuggers allow executing instructions one by one. So you need to find out how variables are alligned on stack and when exactly are they overwritten. Analyzing scanf is hard for beginners, so you can start with the simplified version of your program: replace scanf with the following (can't test, but should work):
*((long long *)(&c)) = 1; //or any other user specified value
*((long long *)(&d)) = 2;
*((long long *)(&l)) = 3;

Hollerith data statements

The below refers to fortran 66 code. Trying to recompile very old programs. Cannot understand the reason for the error. Need work around recommendations. trying not to recode in updated fortran.
DATA NPOP/6HPOPS-I,6HPOPS-D,6HPOPS-C,6HPOPS-A,6HPOPS-E,6HPOP-S /
^
Truncating characters on right side of hollerith constant at (^) azthree.for:24:
DATA NPOP/6HPOPS-I,6HPOPS-D,6HPOPS-C,6HPOPS-A,6HPOPS-E,6HPOP-S /
^
Truncating characters on right side of hollerith constant at (^) azthree.for:24:
DATA NPOP/6HPOPS-I,6HPOPS-D,6HPOPS-C,6HPOPS-A,6HPOPS-E,6HPOP-S /
^
I've never used Fortran66, but Hollerith constants declare the next characters as data, not code. So 6H would mean something like: The 6 characters after the H are supposed to be data, not code.
In essence, I would think that 6HPOPS-I would be the same as "POPS-I".
Maybe if you could give us the declaration of NPOP that would help to understand the issue.
I have kind of recreated the error message with this code:
PROGRAM holl
IMPLICIT NONE
CHARACTER*1 NPOP(6)
INTEGER i
DATA NPOP / 6HPOPS-I,6HPOPS-D,6HPOPS-C,
+ 6HPOPS-A,6HPOPS-E,6HPOP-S /
DO 100 i = 1, 6
PRINT *, NPOP(i)
100 CONTINUE
END PROGRAM
This gives the compiler warnings:
$ gfortran -o holl holl.f
holl.f:6.38:
+ 6HPOPS-A,6HPOPS-E,6HPOP-S /
1
Warning: Legacy Extension: Hollerith constant at (1)
holl.f:5.20:
DATA NPOP / 6HPOPS-I,6HPOPS-D,6HPOPS-C,
1
Warning: Initialization string starting at (1) was truncated to fit the variable (1/6)
holl.f:5.29:
DATA NPOP / 6HPOPS-I,6HPOPS-D,6HPOPS-C,
1
Warning: Initialization string starting at (1) was truncated to fit the variable (1/6)
But it's not quite your errors. I don't know which compiler you use, and in your case, the marker for the truncation seems to point to the H itself.
So we really need more info from you:
More code, specifically the declaration of NPOP
Compiler version and -options

gfortran Error: Integer too big for its kind at (1) [duplicate]

This question already has an answer here:
Fortran: Integer too big for its kind
(1 answer)
Closed 7 years ago.
I want to see integer kind number of gfortran
so I write this line
write(*,"(1X,I20,'correspond to kind ',I2)"),11111111111,kind(11111111111)
There will be compilation error says
precision of type test.f90:67:57: Error: Integer too big for its kind
at (1). Th is check can be disabled with the option -fno-range-check
So I tried recompile with -fno-range-check. But it gives result
-1773790777correspond to kind 4
What is wrong? On the other hand, intel fortran gives no error and correct answer
An integer literal without any kind designation is always of the default kind no matter what value you enter1. There is therefore no much sense to even inquire
kind(111111111111)
the kind of any such literal is always the default kind, provided the value is valid. So the same as kind(1).
All integer kinds have a limited range of values. The largest one you can get using
write(*,*) HUGE(1)
Here instead of 1 you can use any other constant or variable of the integer kind you examine. Most often, the default value of HUGE will be 2147483647 which corresponds to 32-bit integers.
To use larger integer literal constants, use larger non-default kinds.
It doesn't matter if you use the methods from Fortran 90:
integer, parameter :: lk = selected_int_kind(15)
write(*,*) 11111111111_lk
or from Fortran 2008
use iso_fortran_env
integer, parameter :: lk = int64
write(*,*) 11111111111_lk
Both will work. Of course kind(11111111111_lk) will return the value of lk.
1 That is in standard Fortran. Some compilers may promote a large value to a larger kind for you, but only as a non-standard extension. You may be unpleasantly surprised when moving to a compiler, which keeps the standard behaviour.
There may be a better explanation, but this is how I understand it. The short answer is the compiler defaults to a 4 byte integer.
Fortran is statically typed and you have not declared a variable type. The compiler is forced to use the default integer kind, 4 bytes in this case. The kind function simply returns the 'kind' of integer used. The compiler is letting you know that you are trying to assign a value too large for a 4 byte integer. When you apply -fno-range-check the compiler ignores this fact and the value overflows, thus the negative value returned. You can specify that the default integer kind be 8 bytes, -fdefault-integer-8. See the gfortran docs
Example foo.f90:
program foo
write(*,"(1X,I20,' correspond to kind ',I2)"),111111111111,kind(111111111111)
write(*,"(1X,I20,' correspond to kind ',I2)"),11,kind(11)
end program foo
Compiled with:
$ gfortran -fdefault-integer-8 -o foo.exe foo.f90
$ foo
Results in:
111111111111 correspond to kind 8
11 correspond to kind 8
So you can see the compiler is indifferent to the actual value you are testing.
However, I don't think this gets at the root of what you are trying to do, which I assume is to discover the minimum size of integer necessary for a specific numeric value. I don't know of a way to do this off hand with fortran. See this here and here for solutions you might be able to port from C. The second approach looks promising. In a dynamically typed language like Python the type assignment is handled for you.
>>> type(111111111111)
<type 'long'>
>>> type(11111)
<type 'int'>

Trying to use netlib code (QUADPACK). What is xerror?

I'm trying to figure out how to use quadpack.
In a single folder, I located the contents of "qag.f plus dependencies" and the code blow as qag_test.f:
(maybe this code itself is not very important. This is in fact just a snippet from the quadpack document)
REAL A,ABSERR,B,EPSABS,EPSREL,F,RESULT,WORK
INTEGER IER,IWORK,KEY,LAST,LENW,LIMIT,NEVAL
DIMENSION IWORK(100),WORK(400)
EXTERNAL F
A = 0.0E0
B = 1.0E0
EPSABS = 0.0E0
EPSREL = 1.0E-3
KEY = 6
LIMIT = 100
LENW = LIMIT*4
CALL QAG(F,A,B,EPSABS,EPSREL,KEY,RESULT,ABSERR,NEVAL,
* IER,LIMIT,LENW,LAST,IWORK,WORK)
C INCLUDE WRITE STATEMENTS
STOP
END
C
REAL FUNCTION F(X)
REAL X
F = 2.0E0/(2.0E0+SIN(31.41592653589793E0*X))
RETURN
END
Using gfortran *.f (installed as MinGW 64bit), I got:
C:\Users\username\AppData\Local\Temp\ccIQwFEt.o:qag.f:(.text+0x1e0): undefined re
ference to `xerror_'
C:\Users\username\AppData\Local\Temp\cc6XR3D0.o:qage.f:(.text+0x83): undefined re
ference to `r1mach_'
(and a lot more of the same r1mach_ error)
It seems r1mach is a part of BLAS (and I don't know why it's not packaged in here but obtained as "auxiliary"), but what is xerror?
How do I properly compile this snippet in my environment, Win7 64bit (hopefully without Cygwin)?
Your help is very much appreciated.
xerror is an error reporting routine. Looking at the way it is called, it appears to use Hollerith constants (the ones where "foo" is written as 3hfoo).
if(ier.ne.0) call xerror(26habnormal return from qag ,
* 26,ier,lvl)
xerror in turn calls xerrwv, passing along the arguments (plus a few more).
This was definitely written before Fortran 77 became widespread.
Your best bet would be to use a compiler which still supports Hollerith constants, pull in all the dependencies (xeerwv has a few more, I don't know why you didn't get them from netlib) and run it through the compiler of your choice. Most compilers, including gfortran, support Hollerith; just ignore the warnings :-)
You will possibly need to modify one routine, that is xerprt. With gfortran, you could write this one as
subroutine xerprt(c,n)
character(len=1), dimension(n) :: c
write (*,'(500A)') c
end subroutine xerprt
and put this one into a separate file so that the compiler doesn't catch the rank violation (I know, I know...)

Help with translating this assembly into c

My compiler won't work with an assembly file I have and my other compiler that will won't work with the c files I have. I don't understand assembly. I need to move this over but I'm not getting anywhere fast. Is there someone out there that can help? I can't believe there isn't a translator available. Here is the beginning of the file:
list p=18F4480
#include <p18F4480.inc>
#define _Z STATUS,2
#define _C STATUS,0
GLOBAL AARGB0,AARGB1,AARGB2,AARGB3
GLOBAL BARGB0,BARGB1,BARGB2,BARGB3
GLOBAL ZARGB0,ZARGB1,ZARGB2
GLOBAL REMB0,REMB1
GLOBAL TEMP,TEMPB0,TEMPB1,TEMPB2,TEMPB3
GLOBAL LOOPCOUNT,AEXP,CARGB2
LSB equ 0
MSB equ 7
math_data UDATA
AARGB0 RES 1
AARGB1 RES 1
AARGB2 RES 1
AARGB3 RES 1
BARGB0 RES 1
BARGB1 RES 1
BARGB2 RES 1
BARGB3 RES 1
REMB0 RES 1
REMB1 RES 1
REMB2 RES 1
REMB3 RES 1
TEMP RES 1
TEMPB0 RES 1
TEMPB1 RES 1
TEMPB2 RES 1
TEMPB3 RES 1
ZARGB0 RES 1
ZARGB1 RES 1
ZARGB2 RES 1
CARGB2 RES 1
AEXP RES 1
LOOPCOUNT RES 1
math_code CODE
;---------------------------------------------------------------------
; 24-BIT ADDITION
_24_BitAdd
GLOBAL _24_BitAdd
movf BARGB2,w
addwf AARGB2,f
movf BARGB1,w
btfsc _C
incfsz BARGB1,w
addwf AARGB1,f
movf BARGB0,w
btfsc _C
incfsz BARGB0,w
addwf AARGB0,f
return
I get that I can largly exclude the first two lines as the device defines are in my main.c anyway. The two #defines are just that, but the simplest way (I think) is to just replace instances of _Z and _C with STATUS,2 and STATUS,0 accordingly. The next lines (the GLOBALs) are simply variable declarations I'm gathering. Same with LSB and MSB except they also assign values. The next chunck I think I just declare a bunch of integers with those names (AARGB0, etc) and then the chunck after that is a function.
I don't even bother to translate that function, because my compiler has #asm/#end asm directives so I can put it in raw (as long as its wrapped in a function).
I think I have it all... until I build and my compiler screams about STATUS not being defined. And of course its not. But what is it? I read on the net that STATUS registers are something special but I really don't get how it works.
If you haven't noticed, I'm not even sure what it is I'm really asking. I just want the bloody thing to work.
Your compilers are refusing your source?
Either you are using broken tools, or your source files are buggy. In both cases, your problem is not "translating ASM to C" or something like that, but the bugs in your source / toolchain. Do not try to work around problems, solve them.
STATUS is a built-in register in the PIC architecture, that implements the same functionality as the "status" or "condition" flags register in most larger CPU:s. It contains bitflags that are set and cleared by the microcontroller as it executes code, telling you the result of operations.
The Z flag, for instance, is set whenever an arithmetic operation results in a zero, and the C (carry) flag is set when arithmetic overflow is detected.
These are flags that are typically not visible from C, as C doesn't want to require that the host processor even has status bits, so directly translating this code to C will be hard. You will need to figure out a way to include the status-reading bit tests in the C code, and use those instructions when possible. This might be troublesome, as from C you have less control over which registers are being used, which in turn might make it hard to make sure you're checking the proper flags in the right place(s).
Here are a few links to other people's extended precision PIC code. Most seem to remain in assembly, but they might still be useful as references or inspiration.
You can try to reverse-engineer the disassembly. But what will you learn?
You should be able to compile your assembly (using compiler 1) into an object file, and link that to the object file compiled by compiler 2 from your C file.