Reputation: 476
Is there an equivalent for the 'a' format specifier known from C in Fortran?
C Example:
printf("%a\n",43.1e6); // 0x1.48d3bp+25
Exporting floating point numbers in hexadecimal format prevents rounding errors. While the rounding errors are usually negligible, it is still advantageous to be able to restore a saved value exactly. Note, that the hexadecimal representation produced by printf is portable and human readable.
How can I export and parse floating point numbers in Fortran like I do in C using the 'a' specifier?
Upvotes: 1
Views: 999
Reputation: 91
Fortran 2018 standard introduced 'EX' descriptor which does exactly what '%a' in C. Recent Intel ifort compiler does support that, GNU Fortran - not yet as of version 12.2.0
Upvotes: 1
Reputation:
Another option would be to let the C library do your work for you and interface via C binding. This rather depends on a modern compiler (some F2003 features used).
module x
use, intrinsic :: iso_c_binding
private
public :: a_fmt
interface
subroutine doit(a, dest, n) bind(C)
import
real(kind=c_double), value :: a
character(kind=c_char), intent(out) :: dest(*)
integer, value :: n
end subroutine doit
end interface
interface a_fmt
module procedure a_fmt_float, a_fmt_double
end interface a_fmt
contains
function a_fmt_float(a) result(res)
real(kind=c_float), intent(in) :: a
character(len=:), allocatable :: res
res = a_fmt_double (real(a, kind=c_double))
end function a_fmt_float
function a_fmt_double(a) result(res)
real(kind=c_double), intent(in) :: a
character(len=:), allocatable :: res
character(len=30) :: dest
integer :: n
call doit (a, dest, len(dest))
n = index(dest, achar(0))
res = dest(1:n)
end function a_fmt_double
end module x
program main
use x
implicit none
double precision :: r
integer :: i
r = -1./3.d0
do i=1,1030
print *,a_fmt(r)
r = - r * 2.0
end do
end program main
#include <stdio.h>
void doit (double a, char *dest, int n)
{
snprintf(dest, n-1, "%a", a);
}
Upvotes: 1
Reputation:
If you want to have full precision, the best way is to use unformatted files, such as this:
program main
real :: r
integer :: i
r = -4*atan(1.)
open(20,access="stream")
write (20) r
close(20)
end program main
(I used stream access, which is new to Fortran 2003, because
it is usually less confusing than normal unformatted access). You can then use, for example, od -t x1 fort.20
to look at this as a hex dump.
You can also use TRANSFER
to copy the bit pattern to an integer and then use the Z edit descriptor.
If you really want to mimic the %a specifier, you'll have to roll your own. Most machines now use IEEE format. Use TRANSFER
for copying the pattern to an integer, then pick that apart using IAND
(and multiplications or divisions by powers of two for shifting).
Upvotes: 1