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.
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
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
This works for me
real :: areal
then
write(*,'(i3.3,f0.6)') int(areal),areal-int(areal)
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With