The strange syntax of `write(*,’(A,$)’)` used in the calculation of the factorial

I noticed the following code snippet here:

program torial
    implicit none
    integer, parameter :: IK = selected_int_kind(18)
    integer(IK) :: i
    write(*,'(A,$)') 'Number? : '
    read(*,*) i
    write(*,*) 'Factorial is ',factorial(i)


    recursive function factorial( n ) result (f)
        integer(IK) :: f, n
        if( n == 1 ) then
          f = 1
          f = n * factorial( n - 1_IK )
        end if
    end function factorial
end program torial

What’s the meaning of the write(*,'(A,$)') used above?


A common extension at one time that there is now a standard implementation for. Use

write(*,'(a)',advance='no')'Number? : '

now. The “$” surpressed advancing to a new line after writing the output, the same as
now provided by “advance=‘no’”.


Note that this $ looks like the $ that marks the end of a line in a regular expression.

Thank you for your explanation. I find the following description in Intel ® Fortran Compiler Classic and Intel ® Fortran Compiler Developer Guide and Reference:

$ rga -A4 "advance='no'.{1,20}end-of-record"
For formatted input using nonadvancing I/O (ADVANCE='NO'), an end-of-record (EOR) condition is
returned. If the file was opened with PAD='YES', additional fields are read as spaces.
For list-directed input, another record is read.
For NAMELIST input, another record is read.
For unformatted input, an error occurs.

Another problem is that the code always returns error results for large integers, as follows:

Any hints for fixing this problem?


50! is ~ 3.04E+064
It doesn’t fit into an integer with a range of 18 decimal digits.
Even a 128 bit integer (range = 38) cannot store such a huge number.
A 64 bit real is needed here.

And then you may want to use the intrinsic gamma function instead.

I tried but still failed:

Another question: What’s the meaning of '(a)' used above?


That’s the formatting string, which describes what you will provide after the parenthesis. In this case you tell it there will be a single string (a).

Have a look at Fortran - Basic Input Output for further information.

It’s because your default reals are 32 bits.
You need:

use iso_fortran_env, only: real64
real(real64) :: x 

I also figured it out with the following query:

$ rga -I -A1 -U "^[ ]*A\n.{1,20}character"| awk '!a[$0]++'

Transfers character or Hollerith

But what’s the Hollerith values as mentioned above?

Yes. The following code snippet works:

program test_gamma

    use iso_fortran_env, only: real64
    real(real64) :: x

    write(*,'(a)',advance='no') 'Number: '
    read(*,*) x
    write(*,*) 'Factorial is ', gamma(x+1)
end program test_gamma

For more information, see the following screenshot:

But as you can see, the result is displayed in scientific notation, but I want to show full integer digits. How to achieve this goal?

You can’t, because it’s a floating point 64 bits real: you will have at max 16 digits.
If you want to compute big factorials, you will need a Fortran library dealing with arbitrary integer size. Python is adapting the number of bytes needed to code an integer, Fortran is using fixed size integers (generally 8, 16, 32, 64 bits).

1 Like

Such as MPFUN2020 by David H. Bailey or FM by David M. Smith.

1 Like

Since gamma(x) = (x-1)!, you need to add one to x:

write(*,*) 'Factorial is ', gamma(x+1)

Thank you for pointing out my mistake. I have made relevant correction.

Thank you for letting me know about these tools/packages/libraries.

Bock in the days before CHARACTER type in Fortran, you could initialize data values with Hollerith values. It was a way of saying “The next n characters should be treated like data”. The 16H references below says use the next 16 characters to fill x(1) and x(2). It was about the only way to create character data in 1966. It was also allowed in FORMAT statements to label output.

      double complex x(2) 
      data x /16Habcdefghijklmnop, 16Hqrstuvwxyz012345/ 
      write( 6, '(4A8, "!")' ) x