Silverfrost Forums

Welcome to our forums

reading reals from bytes

19 Nov 2012 7:10 #11092

The 'program test_read_real_from_bytes' below works as free-standing code.

Essentially the same code in 'real_in4_w' below runs fine in /check /undef mode, but not otherwise. The Run-time Error is #67.

The code stops after ' write (iot,'( '' char bytes = ichar '' )' ) '. It correctly lists the four ichar values as four ichar bytes : 103 148 19 68

Any suggestions as to what I'm doing wrong?

PS The convoluted procedure of extracting the ichar values and then reforming the chars into rbyt was simply to get a handle on what the code was doing. I get the same error message if I bypass the ichar / char steps and try to read xval from the input dbyt values.

  program test_read_real_from_bytes

  character *4   dbyt, rbyt

  integer*2      ich_dbyt (4)

  real *4  xval, zval

  iot = 7

  open ( iot, file = 'test_read_real_from_bytes_out.txt' )

c Sample rate: 590.318787 Hz

  ich_dbyt(1) = 103
  ich_dbyt(2) = 148
  ich_dbyt(3) =  19
  ich_dbyt(4) =  68

  write (iot,'(/ '' four ichar bytes : '', 4i10 )' )
 1                ( ich_dbyt(ie), ie=1,4 )

  do ie = 1, 4

     dbyt(ie:ie) = char ( ich_dbyt(ie) )

  end do
  

c write (iot,'( '' four char bytes : '', 4a )' ) c 1 ( dbyt(ie:ie), ie=1,4 )

  do ie = 1, 4
     rbyt(ie:ie) = dbyt(ie:ie)
  end do

  read ( rbyt, '(4a)' ) zval

  write (iot,'( '' value : '', f10.6 )' ) zval 

  stop
  end

four ichar bytes : 103 148 19 68 value : 590.318787

  subroutine real_in4_w ( dbyt, zval )

  character * ( * )  dbyt

  integer*2  ich_dbyt (4)

  character *4  rbyt

  real *4  xval

  iot = 6

  do ie = 1, 4

     ich_dbyt(ie) = ichar ( dbyt(ie:ie) )

  end do
        
  write (iot,'(/ '' four ichar bytes : '', 4i10 )' )
 1                ( ich_dbyt(ie), ie=1,4 )

  write (iot,'( '' char bytes = 0 '' )' )

  do ie = 1, 4

     rbyt(ie:ie) = char ( 0 )

  end do

  do ie = 1, 4

     rbyt(ie:ie) = char ( ich_dbyt(ie) )

  end do

  write (iot,'( '' char bytes = ichar '' )' )

  read ( rbyt, '(4a)' ) xval

  write (iot,'( '' value : '', f10.6 )' ) xval 

  zval = xval
 
  return
  end
19 Nov 2012 9:24 #11096

The problem only occurs when I set the /dreal flag. The program runs fine with no flags, and with /intl

19 Nov 2012 1:28 #11098

The second program is incomplete, since we do not know how the subroutine is invoked. Therefore, one has to play guessing games in an attempt to reproduce the error that was reported.

Add IMPLICIT NONE to each subprogram (or use the corresponding compiler switch) and note what options such as /DREAL do to variables with implicitly declared types and kinds.

19 Nov 2012 11:59 #11108

Norm,

What is the following line doing ? read ( rbyt, '(4a)' ) zval

zval is real*4, although you appear to be storing character values. This looks like very old fortran, which should be better done if zval was declared as character zval(4)*1 Or have I got this wrong ?

( This looks like a way of writing a real*4 in a compact form and retain maximum precision.) Would equivalencing zval to rbyt or perhaps using the intrinsic TRANSFER be better (more standard conforming?) to achieve what you want.

Also, does writing char(0) into a file create a problem when reading or writing characters ?

John

The following code demonstrates the use of EQUIVALENCE: program test_read_real_from_bytes

character*4 dbyt, rbyt
integer*4   ie,iot
integer*2   ich_dbyt (4) 
real*4      xval, zval 
equivalence (xval, dbyt)

 iot = 1
! open ( iot, file = 'test_read_real_from_bytes_out.txt' ) 

!c Sample rate: 590.318787 Hz 

 ich_dbyt(1) = 103 
 ich_dbyt(2) = 148 
 ich_dbyt(3) = 19 
 ich_dbyt(4) = 68 

 write (iot,'(/ a, 4i10 )' )  ' four ichar bytes : ', ( ich_dbyt(ie), ie=1,4 ) 

 do ie = 1, 4 
   dbyt(ie:ie) = char ( ich_dbyt(ie) ) 
 end do 
 write (iot,'( a, f10.6, a )' ) ' xvalue : ', xval , ' Value from equivalence to dbyt'

!c write (iot,'( '' four char bytes : '', 4a )' ) 
!c 1 ( dbyt(ie:ie), ie=1,4 ) 

 do ie = 1, 4 
   rbyt(ie:ie) = dbyt(ie:ie) 
 end do 

 read ( rbyt, '(4a)' ) zval 

 write (iot,'( a, f10.6 )' ) ' zvalue : ', zval 

 call real_in4_w ( dbyt, zval ) 
 call real_in4_wa ( dbyt, zval ) 
 stop 
end 

! four ichar bytes : 103 148 19 68 
! value : 590.318787 



subroutine real_in4_w ( dbyt, zval ) 

character * ( * ) dbyt 
real*4    zval
!
integer*2 ich_dbyt (4) 
integer*4 ie, iot
character *4 rbyt 
real *4 xval 

 iot = 1
 write (iot,*) 'Entering real_in4_w'
!
 do ie = 1, 4 
   ich_dbyt(ie) = ichar ( dbyt(ie:ie) ) 
 end do 

 write (iot,'(/ a, 4i10 )' )  ' four ichar bytes : ', ( ich_dbyt(ie), ie=1,4 ) 

 write (iot,'( a )' ) ' char bytes = 0 '

 do ie = 1, 4 
   rbyt(ie:ie) = char ( 0 ) 
 end do 

 do ie = 1, 4 
   rbyt(ie:ie) = char ( ich_dbyt(ie) ) 
 end do 

 write (iot,'( a )' ) ' char bytes = ichar '

 read ( rbyt, '(4a)' ) xval 

 write (iot,'( a, f10.6 )' ) ' value : ', xval 

 zval = xval 

 return 
 end

subroutine real_in4_wa ( dbyt, zval ) 

character * ( * ) dbyt 
real*4    zval
!
integer*2 ich_dbyt (4) 
integer*4 ie, iot
character *4 rbyt 
real *4 xval 
equivalence (xval, rbyt )
!
 iot = 1
 write (iot,*) 'Entering real_in4_wa'
!
 do ie = 1, 4 
   ich_dbyt(ie) = ichar ( dbyt(ie:ie) ) 
 end do 

 write (iot,'(/ a, 4i10 )' )  ' four ichar bytes : ', ( ich_dbyt(ie), ie=1,4 ) 

 write (iot,'( a )' ) ' char bytes = 0 '

 do ie = 1, 4 
   rbyt(ie:ie) = char ( 0 ) 
 end do 

 do ie = 1, 4 
   rbyt(ie:ie) = char ( ich_dbyt(ie) ) 
 end do 

 write (iot,'( a )' ) ' char bytes = ichar '

! read ( rbyt, '(4a)' ) xval 

 write (iot,'( a, f10.6 )' ) ' value : ', xval 

 zval = xval 

 return 
 end
20 Nov 2012 1:12 #11110

Norm,

I changed the code to give an example of using EQUIVALENCE, TRANSFER and your READ approach. All produce the same result. I have always used the EQUIVALENCE approach, although TRANSFER might be more standard conforming. However, I'm not sure how standard conforming this approach is. I have used this in the past to convert between 'little endian' and 'big endian' for unix binary formats by changing the order of the bytes.

Sorry, I just read your second post: doesn't using /dreal convert the real value to 8 bytes ? so you need to provide an 8-byte format for zval. You could find out the layout by reversing the process. such as:

integer*4 i
real*8 zval
character dbyt*8
equivalence (zval,dbyt)
!
zval = 590.318787
write (*,*) zval
write (*,*) (ichar(dbyt(i:i)),i=1,8)
!
end

John

program test_read_real_from_bytes 

character*4 dbyt, rbyt
integer*4   ie,iot
integer*2   ich_dbyt (4) 
real*4      xval, zval 
equivalence (xval, dbyt)

 iot = 1
! open ( iot, file = 'test_read_real_from_bytes_out.txt' ) 

!c Sample rate: 590.318787 Hz 

 ich_dbyt(1) = 103 
 ich_dbyt(2) = 148 
 ich_dbyt(3) = 19 
 ich_dbyt(4) = 68 

 write (iot,'(/ a, 4i10 )' )  ' four ichar bytes : ', ( ich_dbyt(ie), ie=1,4 ) 

 write (iot,*) 'pack byte values into dbyt'
 do ie = 1, 4 
   dbyt(ie:ie) = char ( ich_dbyt(ie) ) 
 end do 
!
 write (iot,'( a, f10.6, a )' ) ' xvalue : ', xval , ' Value from equivalence to dbyt'
!
 zval = 0
 zval = transfer (dbyt, zval)
 write (iot,'( a, f10.6, a )' ) ' zvalue : ', zval , ' Value from TRANSFRE from dbyt'
!
 do ie = 1, 4 
   rbyt(ie:ie) = dbyt(ie:ie) 
 end do 
 read ( rbyt, '(4a)' ) zval 
 write (iot,'( a, f10.6, a )' ) ' zvalue : ', zval , ' Value using read (rbyt, 4a)'

! call real_in4_w ( dbyt, zval ) 
! call real_in4_wa ( dbyt, zval ) 
 stop 
end 
20 Nov 2012 1:34 #11112

Thanks, John. That's neat.

As you know, I'm more of a coder than a programmer. I use Fortran to implement statistical algorithms, and so I reuse the same simple subset of Fortran that's worked in the past.

Someone gave me that bit of code for reading reals from bytes and so I went on using it.

Doesn't equivalence hark back to Fortran 77? Transfer seems more elegant. I assume that it would be insensitive to the byte ordering.

Logic dictates that the problem I was having lies with the real *4 declaration; as you note in your later post, the /dreal flag must be 'doubling' the size of xval.

Out of curiosity, can I somehow declare the size of xval to be 4 bytes, so that /dreal leaves it as declared?

Cheers Norm

20 Nov 2012 2:51 #11113

Norm,

You would need to check /dreal documentation. However, I would NEVER use it, but explicitly declare the type of real. For such an important property, you must have the variable precision explicitly defined in the code. You can go down the path of all the KIND related formats and selected_real_kind parameters, but for me, the simplest approach is to use 'real4' or 'real8'. They clearly document the precision! I think that /dreal only applies to statements like real x, where the byte size is not explicitly defined. I could be wrong, but again I'd strongly recommend against it. I'm assuming you are getting the byte values from an external source, so before you do this, you should know how many bytes are in the real variable and the byte format. Unix (and I think Linux) do use a different byte format. Hence the lovely names of big endian and little endian! All these problems are easily solved by being able to manage the bytes, such as with charactern or with an integer1 array, which is then equivalenced or TRANSFER to the real*n variable. Much better than using a read or write statement. The format (number of bytes and order) of the real you are being supplied (or supplying) is a key issue to clarify. The conversion is easy! If they mix 4-byte and 8-byte values, then it becomes a bit more complex, but there is always a pre-defined structure to scan.

John

20 Nov 2012 3:05 #11114

Hi John

I agree totally with your comments re real*4 -- it's always made perfect sense to me to simply declare the number of bytes in the real.

Perhaps Paul can tell an old-fashioned coder how to do it so that /dreal doesn't change the declaration, if that's possible.

The file contains a whole lot of backscatter and related values from the water column and seafloor. The file format is very clearly set out, so the numbers of bytes for the various variables are all known. That's why /dreal is stuffing things up (to put it bluntly), since there are 4 bytes for all the reals.

Cheers Norm

20 Nov 2012 3:13 #11116

Norm, You could separate out the routines for the conversion to a seperate file and not select /dreal for these routines (this file). As I posted recently, I have a batch file (make.bat) which explicitly compiles each file with it's particular compiler options, then runs 'SLINK <link_list.txt' I have about 50 .f95 ( > .obj) files, plus about 6 libraries (.lib) im a typical program build. I use /debug for most, /opt for high performance routines, or /check for more robust reading of data from files. ( using /opt and /check in the same executable is not a good idea, but now we can mix /debug and /opt and still get access to 3.8gb of memory )

John

20 Nov 2012 8:38 #11119

Using John's transfer approach you can encapsulate the conversion both ways in a couple of subroutines:

SUBROUTINE real_to_bytes(value, bytes)
   REAL*4, INTENT(IN) :: value
   CHARACTER, INTENT(OUT) :: bytes(4)
   bytes = TRANSFER(value, bytes))
END SUBROUTINE real_to_bytes

SUBROUTINE bytes_to_real(bytes, value)
   CHARACTER, INTENT(IN) :: bytes(4)
   REAL*4, INTENT(OUT) :: value
   value = TRANSFER(bytes, value)
END SUBROUTINE bytes_to_real

You can use a module and an INTERFACE to handle different kinds of reals

MODULE real_convertor

   INTERFACE real_to_bytes
      MODULE PROCEDURE real4_to_bytes
      MODULE PROCEDURE real8_to_bytes
   END INTERFACE real_to_bytes

   INTERFACE bytes_to_real
      MODULE PROCEDURE bytes_to_real4
      MODULE PROCEDURE bytes_to_real8
   END INTERFACE bytes_to_real

CONTAINS

   SUBROUTINE real4_to_bytes(value, bytes)
      REAL*4, INTENT(IN) :: value
      CHARACTER, INTENT(OUT) :: bytes(:)
      bytes = TRANSFER(value, bytes))
   END SUBROUTINE real4_to_bytes

   SUBROUTINE real8_to_bytes(value, bytes)
      REAL*8, INTENT(IN) :: value
      CHARACTER, INTENT(OUT) :: bytes(:)
      bytes = TRANSFER(value, bytes))
   END SUBROUTINE real8_to_bytes

   SUBROUTINE bytes_to_real4(bytes, value)
      CHARACTER, INTENT(IN) :: bytes(:)
      REAL*4, INTENT(OUT) :: value
      value = TRANSFER(bytes, value)
   END SUBROUTINE bytes_to_real4

   SUBROUTINE bytes_to_real8(bytes, value)
      CHARACTER, INTENT(IN) :: bytes(:)
      REAL*8, INTENT(OUT) :: value
      value = TRANSFER(bytes, value)
   END SUBROUTINE bytes_to_real8

END MODULE real_convertor
Please login to reply.