Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pad Fortran floating point output with leading zeros?

I have some floating point numbers that I need to output from a Fortran program. Let's say the maximum number could be 999.9999 and they are all non-negative. I need zero-padding in front of all numbers less than 100.

For instance, if I have 25.6893782, 245.354567, and 1.2345678, I need to print them out in a form something like

025.6894
245.3546
001.2346

How can I do this? It would be fairly easy with the T edit descriptor if I knew that, for instance, all numbers would be between 10 and 99, something like that. But there is no way for me to know that ahead of time.

like image 904
bob.sacamento Avatar asked Jul 26 '13 16:07

bob.sacamento


4 Answers

This is a trick I used to use in MS BASIC on the Commodore PETs in the late 70s. The code has been modified for negative numbers. If you would like positive numbers to have a leading +, just change the last character of signchr to '+'

subroutine leadingzero(x)
   real x
   character(len=16) str
   character, dimension(3):: signchr = (/'-', ' ', ' ' /)
   write(str,'(F9.4)') 1000.0 + abs(x)
   write(*,*) signchr(int(sign(1.0,x)) + 2), str(2:) ! drop the 1
end subroutine leadingzero

program main
   call leadingzero(0.01)
   call leadingzero(0.1)
   call leadingzero(2.532)
   call leadingzero(9.9999)
   call leadingzero(9.999999)
   call leadingzero(10.987)
   call leadingzero(123.456)
   call leadingzero(0.0)
   call leadingzero(-0.01)
   call leadingzero(-0.1)
   call leadingzero(-2.532)
   call leadingzero(-9.9999)
   call leadingzero(-9.999999)
   call leadingzero(-10.987)
   call leadingzero(-123.456)
end program

Edit - returning result in a string

subroutine leadingzerostr(x, str_temp)
    real x
    character(*) str_temp
    character(len=10) str
    character, dimension(3):: signchr = (/'-', ' ', ' ' /)
    write(str,'(F10.4)') 10000.0 + abs(x)
    str_temp = str
    str_temp(1:1) = signchr(int(sign(1.0,x)) + 2)
end subroutine leadingzerostr
like image 53
cup Avatar answered Nov 19 '22 11:11

cup


An alternative method to the method of High Performance Mark is to do use the TL and TR position-edit-descriptors. First print the float with Fw.d, move w positions back, print the integer with padding zeros and width w − d, move d + 1 positions forward.

write (*, '(F6.3,TL6,I2.2,TR4)') f,int(f)

The problem this method and the method of High Performance Mark have is rounding. The following program demonstrates this:

program test_rounding
  double precision :: f
  f = 6 - 1D-6
  ! default compiler dependent rounding :: gfortran NEAREST
  write (*, '(F6.3,TL6,I2.2,TR4)') f,int(f)
  write (*, '(I2.2,F0.3)') int(f), f-int(f)
  write (*, '(I2.2,F4.3)') int(f), f-int(f)
  ! rounding to ZERO
  write (*, '(I2.2,RZ,F4.3)') int(f), f-int(f)
  write (*, '(RZ,F6.3,TL6,I2.2,TR4)') f,int(f)
end program
05.000          < WRONG
051.000         < VERY WRONG
05****          < EUH
05.999          < OFF BY 0.001
05.999          < OFF BY 0.001

The last method might be of interest, but it is not really the expected value. However, it has the same accuracy.

The following two methods work as expected, but it is required to do manual manipulate the numbers with the respected accuracy. This is not what one would expect:

program test_rounding
  double precision :: f
  f = 6 - 1D-6
  ! manual manipulation
  write (*,'(I2.2,".",I3.3)') nint(f*1D3)/1000, mod(nint(f*1D3),1000)
  write (*,'(F6.3,TL6,I2.2,TR4)') f,nint(f*1D3)/1000
end program

Both return 06.000

like image 29
kvantour Avatar answered Oct 17 '22 07:10

kvantour


This works for me

real :: areal

then

write(*,'(i3.3,f0.6)') int(areal),areal-int(areal)
like image 11
High Performance Mark Avatar answered Nov 19 '22 11:11

High Performance Mark


Zero-padding can be performed for integer fields, so if you printed the result as two separate fields you might be able to make it happen. Here's a way that is less than pretty, but works. Say x is the value you want to print:

DOUBLE PRECISION x
CHARACTER*6 y

x = 123.4567

WRITE(y,'(F6.4)') x-int(x)


WRITE(*,'(I3.3,A5)') int(x), y(2:5)

y is declared as a CHARACTER*6 because it needs to hold the fractional part of your number (4 decimal places), a leading zero, and a decimal point. This can be easily changed if you want to show more decimal places, though it would be trickier if you wanted to show a variable number of decimal places.

The I3.3 field descriptor means "print an integer with a maximum field width of 3 and pad left with zeroes so that there are always 3 digits". When printing out the value we take y(2:5) to strip off the leading zero.

Happy coding!

like image 2
c.anna Avatar answered Nov 19 '22 11:11

c.anna