Search code examples
phpfortranpack

Converting Fortran REAL packing routine to PHP


I am tasked with converting some Fortran code to PHP and am stumbling at the last hurdle.

In essence the Fortran converts a REAL into a binary CHAR(4) which it ultimately writes to file.

The Fortran (which also confuses me) is as follows:

FUNCTION MKS(x)
    CHARACTER (LEN=4) :: MKS             ! The 4-character string which is returned to 
    REAL :: x                            ! The incoming single-precision variable 
    CHARACTER (LEN=1), DIMENSION(4) :: a ! A working variable
    CHARACTER (LEN=4) :: d               ! A working variable
    CALL MKS1(x,a)                       ! Send x - get back a(1), a(2), a(3), a(4) 
                                    ! Note: x will hold the first 32 bits referenced 
                                         ! and a will hold the next 32 bits
d = a(1) // a(2) // a(3) // a(4)     ! concatenate into 1 string (d)
MKS = d                              ! assign string to variable MKS
END FUNCTION MKS

SUBROUTINE MKS1 (b,c)
    IMPLICIT NONE
    CHARACTER (LEN=1), DIMENSION(4)  :: b    ! array with incoming 32 bits
    CHARACTER (LEN=1), DIMENSION(4)  :: c    ! array with each character returned
    INTEGER :: i                             !  DO Loop counter
    DO i=1,4
        c(i) = b(i)
    END DO
END SUBROUTINE MKS1

I have attempted to recreate this function using php as follows

pack('CCCC', $value & 0x000F, 
           ($value>>8) & 0x000F, 
           ($value>>16) & 0x000F, 
           ($value>>24) &0x000F);

But on comparing the output values using the *nix od command shows completely different results.

What is the correct way to pack the equivalent to a Fortran REAL into a char[4] Array in PHP?


Solution

  • It turned out to be quite simple.

    Your FORTAN REAL is stored as an IEEE 754 32 bit floating point number.

    The output from your od was misleading. Converting it to hex gives the following.

    0115040 0134631 0005077
    
    0x20, 0x9A, 0x99, 0xB9, 0x3f, 0x0a
    

    The first and last bytes of the file are redundant, they are a space and a carriage return respectively. The bit we're after is the middle 4 bytes.

    Using pack we can convert from floats (warning - endianness is machine dependant).

    The following:

    var_dump(bin2hex(pack('f', 1.450)));
    

    Gives us a familar sequence of bytes.

    string(8) "9a99b93f"
    

    So instead of converting to hex, output that to a file with a space at the start and a carriage return at the end, and you'll have an identical file. (as long as your PHP/machine configuration doesn't do something mad with floats - but even then if you follow the IEEE 754 spec, you should be able to reproduce it)