My (unformatted) output looks typically like
.... 0. 0. 0. 0. 0. 0. 0.7875317755557483 0.7862384282167548
0.784890147719363 ....
Many values are zero, but others are not. To save storage space I
would like it to output
.... 0. 0. 0. 0. 0. 0. 0.7875 0.7862 0.7849 ....
With the edit descriptor f6.4, it would look like
.... 0.0000 0.0000 0.0000 0.0000 0.0000 0.000 0.7875 0.7862
0.7849 ....
which is not space efficient either.
Is there a way to get a fixed number of digits, but avoid the trailing
zeros when the output is exactly zero?
> Is there an edit descriptor in Fortran that suppresses trailing zeros,
> but outputs significant digits when not zero?
>
> My (unformatted) output looks typically like
> .... 0. 0. 0. 0. 0. 0. 0.7875317755557483 0.7862384282167548
> 0.784890147719363 ....
> Many values are zero, but others are not. To save storage space I
> would like it to output
> .... 0. 0. 0. 0. 0. 0. 0.7875 0.7862 0.7849 ....
That looks very formatted as it is human legible. I think you mean
free formatted. Unformatted is the native machine form that often
goes by the street name of binary. The terminology is that of Fortran
rather than the street. Precision is neded here to talk to others for
advice and to find things in the manual.
Unformatted means there is NO format, which includes the list format
of "*" that is just the compilers choice rather than yours.
> With the edit descriptor f6.4, it would look like
> .... 0.0000 0.0000 0.0000 0.0000 0.0000 0.000 0.7875 0.7862
> 0.7849 ....
> which is not space efficient either.
>
> Is there a way to get a fixed number of digits, but avoid the trailing
> zeros when the output is exactly zero?
If you are using F90 then you need to URGENTLY (as in right now!) reread
the section on the advance = "no" option of write. The result will have
full control over the output but will be more verbose.
write ( 10, "(10f20.8)" ) ( x(i), i = 1, 10 )
becomes
do i = 1, 10
if ( x(i) == 0.0 ) then
write ( 10, "(1x,f2.0)", advance = "no" ) x(i)
else
write ( 10, "(1x,f20.8)", advance = "no" ) x(i)
end if
end do
write ( 10, "('')" )
where your details and style will almost surely be something else.
If you have non-advancing I/O you can print numbers one at a
time with any desired format.
if(x.eq.0) then
write(*,'(A)',advance='no') ' 0.0'
else
write(*,'(f7.4)',advance='no') x
endif
You can put that in a loop and add the appropriate record advance
with something like:
write(*,'(1x)')
or
write(*,'(/)',advance='no')
(I am not sure what the best advance but don't write anything
statement is.)
Keep track of the number of columns written and advance
as appropriate.
It is problems like this that have been so easy to do in
other languages that have always had non-advancing I/O,
but hard to do in Fortran.
-- glen
norbe...@gmail.com wrote:
> Is there an edit descriptor in Fortran that suppresses trailing zeros,
> but outputs significant digits when not zero?
...
> Is there a way to get a fixed number of digits, but avoid the trailing
> zeros when the output is exactly zero?
Write the data line to a character string (an internel formatted
WRITE).
Then pass down the string to locate a decimal point at j1, and check
only zeroes till a blank at j2 (else pass this field).
Then move the whole line down from j2 to j1+2 (overwriting by j2-j1-2
zeroes) and pad the same thngth at the far right. This leave the
decimal point and one zero on each zero string field..
Repeat.
Write out the new string, either full original length or the new
shorter version.
This method avoids needing non-advancing I/O.
Norb,
I wanted to do the same thing a while back, and I did not find any
standardized edit descriptor to do it. So I wrote an F90 subroutine to
do it. If you would like a copy of the routine, please say so and I
will post it early next week. It's good for making compact CSV output,
among other things.
It uses about the same algorithm that Terrence posted, except that it
also strips the unprotected decimal point if the fraction equals
.000... You could of course tweak it to do exactly what you want, or
roll your own.
--Dave
Make that "Terence" with one "r". Ouch.
You might write into a string, and replace occurrences of "0 " with " "
until all have disappeared before actually printing the string.
This method wastes time as well as being more involved than
using non-advancing output (which seems to fit the bill
in this case).
Sufficient would be
write (10, '(A)' , advance = 'no' ) ' 0.'
> Write the data line to a character string (an internel formatted
> WRITE).
> Then pass down the string to locate a decimal point at j1, and check
> only zeroes till a blank at j2 (else pass this field).
> Then move the whole line down from j2 to j1+2 (overwriting by j2-j1-2
> zeroes) and pad the same thngth at the far right. This leave the
> decimal point and one zero on each zero string field..
> Repeat.
> Write out the new string, either full original length or the new
> shorter version.
This would give very short lines when many zeros are present,
as well as not being particularly fast.
> This method avoids needing non-advancing I/O.
There's no need to avoid non-advancing output.
It provides the simplest method.
This archaic form was superseded nearly 20 years ago
with: if (x == 0) then
> write(*,'(A)',advance='no') ' 0.0'
> else
> write(*,'(f7.4)',advance='no') x
> endif
>
> You can put that in a loop and add the appropriate record advance
> with something like:
>
> write(*,'(1x)')
Sufficient is:- write (*,*)
> or
> write(*,'(/)',advance='no')
>
> (I am not sure what the best advance but don't write anything
> statement is.)
>
> Keep track of the number of columns written and advance
> as appropriate.
It isn't necessary to do that, as new lines are automatically started.
> It is problems like this that have been so easy to do in
> other languages that have always had non-advancing I/O,
> but hard to do in Fortran.
Indeed, it's been available in PL/I and Algol for 40+ years.
In PL/I:
if x(i) ^= 0
then put edit (x(i)) (f(20,16));
else put edit (' 0.')(a);
In PL/I it's possible to write out all the values of X in a single
statement, by making use of the TRIM function (which can also
trim trailing zeros as well as leading blanks) and the EDIT function.
The following statement prints an abbreviated form of x(i)
when zero, and a formatted form when non-zero:-
put edit (trim(edit(x(i), '-9.V999999'), ' ', '0') ) (x(1), a);
This statement can be extended to print all elements
of the array X, as foreshadowed above.
> -- glen
it wasn't actually superceded, it was made redundant with an unnecessary
new form.
<snip>
--
Gary Scott
mailto:garylscott@sbcglobal dot net
Fortran Library: http://www.fortranlib.com
Support the Original G95 Project: http://www.g95.org
-OR-
Support the GNU GFortran Project: http://gcc.gnu.org/fortran/index.html
If you want to do the impossible, don't hire an expert because he knows
it can't be done.
-- Henry Ford
Optimization and illustrating a method are often at odds.
Optimization then obscures the point being illustrated.
In this case illustrating the method was the greater requirement.
The optimization of a purely text format was considered and rejected.
The form write ( 10, "(' 0.')" was considered but was considered
a poor illustration even while being an even better optimiaztion
than the proposed improvement.
where table is a character(len=6) array.
You'll have to populate it with
'0. '
'0.0001'
...
'0.0999'
'0.1 '
'0.1001'
etc.
But, when you trim the values you'll suppress all of the trailing
spaces, not just the ones after 0. .
Like all clever optimizations, the code is difficult for a
human to read.
Dick Hendrickson
One way to do it is to write a separate function that formats each
output string just the way you want and then invoke it in an implied
DO-loop:
C:\gfortran\clf\zeros>type zeros.f90
module mykinds
implicit none
integer, parameter :: dp = selected_real_kind(15,300)
end module mykinds
module funcs
use mykinds
implicit none
contains
function write_fmt(x, fmt)
real(dp) x
character(*) fmt
character(write_fmt_len(x, fmt)) write_fmt
if(x /= 0) then
write(write_fmt, fmt) x
else
write_fmt = '0.'
end if
end function write_fmt
pure function write_fmt_len(x, fmt)
real(dp), intent(in) :: x
character(*), intent(in) :: fmt
integer write_fmt_len
character(256) temp
if(x /= 0) then
write(temp, fmt) x
write_fmt_len = len_trim(temp)
else
write_fmt_len = 2
end if
end function write_fmt_len
end module funcs
program zeros
use mykinds, only: dp
use funcs, only: write_fmt
implicit none
real(dp) A(9)
character(20) fmt
integer i
A = (/0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp, &
0.7875317755557483_dp, 0.7862384282167548_dp, &
0.784890147719363_dp/)
write(fmt,'(a,i0,a)') '(',size(A),'(a:1x))'
write(*,fmt) (write_fmt(A(i),'(f6.4)'), i = 1, size(A))
end program zeros
C:\gfortran\clf\zeros>gfortran zeros.f90 -ozeros
C:\gfortran\clf\zeros>zeros
0. 0. 0. 0. 0. 0. 0.7875 0.7862 0.7849
Actually, the above technically requires f03 to enable invocation of
a function that does internal I/O during execution of a WRITE
statement, but this feature of f03 still works in many f95 compilers.
--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end
It's an archaic form, and has been superseded (=displaced) by the improved form.
Printing 0. is clearer than printing x(i) when x(i) is zero.