# Faster string to double

**URL:** <https://fortran-lang.discourse.group/t/faster-string-to-double/2208>\
**Category:** Uncategorized\
**Created:** [October 31, 2021, 5:35pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208 "2021-10-31T17:35:21Z")\
**Posts on this page:** 20\
**Page:** 3

<div class="post-metadata">

**Author:** ![Arjen](https://avatars.discourse-cdn.com/v4/letter/a/b9bd4f/32.png) [@Arjen](https://fortran-lang.discourse.group/u/Arjen)\
**Post date:** [November 8, 2021, 11:34am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/45 "2021-11-08T11:34:39Z")

</div>

Well tested should include:

- Accurate results
- Identify incorrectly formed strings - would something like “-1 .2” be acceptable? Would this result in -1.0 or -1.2
- The pattern of acceptable strings must be unambiguous and conform to the Fortran standard.

---

<div class="post-metadata">

**Author:** ![msz59](https://avatars.discourse-cdn.com/v4/letter/m/3d9bf3/32.png) [@msz59](https://fortran-lang.discourse.group/u/msz59)\
**Post date:** [November 8, 2021, 10:51pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/46 "2021-11-08T22:51:13Z")

</div>

One can follow one of three paths:

1. the string should be a valid real or integer literal constant. Then “-1 .2” should result in error
2. treated as read with FMT=\* - then an internal space ends processing, “-1 .2” results in -1.0 - this would mimic C’s strtod().
3. treated as read with FMT=’(Fw.0)’ where `w` is the length of the string - then the space would be ignored and the result would be -1.2

As we seek (AFAIU) for a fast replacement of `read(string, *) x`, I’d vote for no. 2, with possible optional argument returning info on whether the whole text was processed or not (as C’s strtod() returns via its second argument).

---

<div class="post-metadata">

**Author:** ![mecej4](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/mecej4/32/855_2.png) [@mecej4](https://fortran-lang.discourse.group/u/mecej4)\
**Post date:** [November 15, 2021, 12:09pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/47 "2021-11-15T12:09:58Z")

</div>

> [@Beliavsky](#):
>
> it appears that gfortran 12 is faster than gfortran 10.2 on this, although still slower than Intel.

I think that there is little difference caused by using Gfortran 10.2 instead of 12. Rather, the differences are attributable to the different RTLs used by Cygwin and MinGW64. I compiled the source to an object file using Gfortran Cygwin 11.2. I then linked that object file using Gfortran 12.0 (MinGW64) and the resulting EXE ran in 6.6 seconds (compared to 30 s with Cygwin).

---

<div class="post-metadata">

**Author:** ![mecej4](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/mecej4/32/855_2.png) [@mecej4](https://fortran-lang.discourse.group/u/mecej4)\
**Post date:** [November 15, 2021, 12:52pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/48 "2021-11-15T12:52:42Z")

</div>

> [@Beliavsky](#):
>
> … on Windows setting n to 10^4 (vs. 10^6 in the original code) `strtod` is very slow, with output

The second block of FortranFan’s code of #4 exposes a performance bug in the MinGW64 library function strtod() on Windows. Building the program with Cygwin and Gfortran 11.2 (Cygwin) gives a run time of 0.26 seconds for the second block. With Gfortran 12.0 (MinGW64) and with NAG Fortran, which uses MINGW, the second block takes “forever”, i.e., I got tired of waiting and aborted the run.

By adding a PRINT statement in the DO loop of the second block, I found that the performance penalty is enormous. The one million iterations of the DO loop take 0.5 s with Gfortran 11.2 (Cygwin), and I estimate 9000 s for NAG FortranMinGW (or Gfortran + MinGW), based on my observed 9 seconds for every 1000 iterations. That is a **ratio of MinGW/Cygwin of 18,000 for run times**!

**Correction/Retraction:** Sorry, there is a bug in the example code that I should have caught and corrected before blaming the library routine strtod(). The C library routine expects null-terminated strings. FortranFan’s code in #4 should have done something similar to

```auto
strings(i) = trim(strings(i)) // char(0)

```

or arranged for null-termination of strings(i) in some other way. Or, have I missed some aspects of the C-interoperability features that provides a null termination to strings?

It can take a long time looking for nulls that were not placed in the appropriate places.

---

<div class="post-metadata">

**Author:** ![Beliavsky](https://avatars.discourse-cdn.com/v4/letter/b/ba8739/32.png) [@Beliavsky](https://fortran-lang.discourse.group/u/Beliavsky)\
**Post date:** [November 15, 2021, 1:10pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/49 "2021-11-15T13:10:19Z")

</div>

Based on your last two messages, it looks like a Windows Fortran programmer trying to optimize should be willing to compile her program with both MinGW and Cygwin versions of gfortran (and also WSL 2 and Intel Fortran).

---

<div class="post-metadata">

**Author:** ![mecej4](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/mecej4/32/855_2.png) [@mecej4](https://fortran-lang.discourse.group/u/mecej4)\
**Post date:** [November 16, 2021, 1:50am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/50 "2021-11-16T01:50:29Z")

</div>

> [@Carltoffel](#):
>
> `equivalence` makes the `character` variable available as an `integer` .

Equivalencing variables of different types (integers and characters, in your code) has been illegal since Fortran 95. EQUIVALENCE itself is marked “obsolescent” in Fortran 2018

---

<div class="post-metadata">

**Author:** ![Carltoffel](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/carltoffel/32/1680_2.png) [@Carltoffel](https://fortran-lang.discourse.group/u/Carltoffel)\
**Post date:** [November 16, 2021, 7:01am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/51 "2021-11-16T07:01:24Z")

</div>

You’re right, but I don’t know any alternative which can compete with the speed of `equivalence`. `transfer` is much slower.

---

<div class="post-metadata">

**Author:** ![jacobwilliams](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jacobwilliams/32/10_2.png) [@jacobwilliams](https://fortran-lang.discourse.group/u/jacobwilliams)\
**Post date:** [May 8, 2022, 3:23am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/52 "2022-05-08T03:23:38Z")

</div>

Some more data points: the NAIF SPICELIB has routines [nparsd](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/spicelib/nparsd.html) (string to double) and [nparsi](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/spicelib/nparsi.html) (string to integer) with decades of heritage. To see the code, you have to download and unzip the library [here](https://naif.jpl.nasa.gov/naif/toolkit_FORTRAN.html). I have found that `nparsd` doesn’t always quite give the same answer to the last bit as the built in Fortran `read`.

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [June 1, 2022, 4:56am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/53 "2022-06-01T04:56:16Z")

</div>

Yes. It’s true. I checked.

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [June 1, 2022, 5:04am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/54 "2022-06-01T05:04:59Z")

</div>

There are in this post some useful codes converting string to double fast and correctly. I’d like to pack everything in one module. Here I provide the module, testing program for

- direct method
- strtod (from C, namely c\_st\_to\_r8)
- f\_strtod (namely f\_st\_to\_r8)
- str2real (_but this one does not work. I put it here for a clear check_. No. The new version works excellently. Read **UPDATE 1** )

Herein we can check the elemental attribution, which allows calling routine with array.

**Result:**

**1. with Ifort (2021.6.0 20220226):**

> N = 1000000 , i0 = 353352  
> Write: time consumed = 0.3052 seconds
> 
> * * *
> 
> BLOCK 1: formatted read toward string to double  
> Read: time consumed = 0.3692 seconds  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1046 seconds  
> Read: time consumed = 0.1037 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0868 seconds  
> Read: time consumed = 0.0861 seconds (array)  
> A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00
> 
> * * *
> 
> BLOCK 4: F str2real  
> Read: time consumed = 0.0395 seconds  
> Read: time consumed = 0.0390 seconds (array)  
> A = Max |rval(:)-rref(:)| = 4.424E+06, B = epsilon(1.0d0) = 2.220E-16, B-A = -4.424E+06  
> \*\*\* WARNING: A \> B, (A-B)/epsilon = 1.992E+22

**2. with Gfortran (8.3.0)**

> N = 1000000 , i0 = 727798  
> Write: time consumed = 0.8102 seconds
> 
> * * *
> 
> BLOCK 1: formatted read toward string to double  
> Read: time consumed = 0.6475 seconds  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1046 seconds  
> Read: time consumed = 0.1038 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0790 seconds  
> Read: time consumed = 0.0797 seconds (array)  
> A = Max |rval(:)-rref(:)| = 1.887E-15, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.665E-15  
> \*\*\* WARNING: A \> B, (A-B)/epsilon = 7.500E+00
> 
> * * *
> 
> BLOCK 4: F str2real  
> Read: time consumed = 0.0416 seconds  
> Read: time consumed = 0.0412 seconds (array)  
> A = Max |rval(:)-rref(:)| = 9.649E+05, B = epsilon(1.0d0) = 2.220E-16, B-A = -9.649E+05  
> \*\*\* WARNING: A \> B, (A-B)/epsilon = 4.345E+21

**Source code:**

```auto
!------------------------------------------------------------------------------
!
   module mod_st_to_dp
!
   use, intrinsic :: iso_c_binding, only: c_double, c_char, c_ptr, &
                                            c_null_ptr, c_long
! 
   use, intrinsic :: iso_fortran_env, only: dp => real64, i8 => int64
!
   implicit none 
!
   private
! 
   public :: f_st_to_r8
   public :: c_st_to_r8
   public :: str2real
!
!
   interface
      pure & 
      function c_strtod ( str, endptr ) result(d) bind(C, name="strtod")
!
!! <stdlib.h> :: double strtod(const char *str, char **endptr)
!
         import :: c_char, c_ptr, c_double
!! Argument list
         character(kind=c_char,len=1), intent(in) :: str(*)
!! type(c_ptr), intent(inout) :: endptr !<- since we need pure attribution.
         type(c_ptr), intent(in) :: endptr
!! function result
         real(c_double) :: d 
      end function 
   end interface
!
!
! integer, parameter :: dp = selected_real_kind( p=12 )
! FP constants
   real(dp), parameter :: ten = 10.0_dp
! Integer constants
   integer, parameter :: ascii_negative = 45
   integer, parameter :: ascii_period = 46
   integer, parameter :: ascii_0 = 48
   integer, parameter :: ascii_9 = 57
   integer(i8), parameter :: ten_int = 10_i8
!
   type(c_ptr),parameter :: endptr = c_null_ptr
!
!------------------------------------------------------------------------------
   contains
!------------------------------------------------------------------------------
   elemental &
   subroutine c_st_to_r8 ( str, r )
      character(len=*), intent(in) :: str
      real(dp), intent(out) :: r
!
      r = c_strtod ( str, endptr )
      return 
   end subroutine 
!------------------------------------------------------------------------------
!
   elemental &
   subroutine f_st_to_r8 ( str, r )
      character(len=*), intent(in) :: str
      real(dp), intent(out) :: r
! local variables
      integer(i8) :: n, n_exp
      integer :: lens, pos_exp, expnt
!
      r = 0.0_dp
      n_exp = 0
      lens = len_trim( str )
      pos_exp = index( str, "E", back=.true. ) ! <-- find the capital 'E'
      if ( pos_exp == 0 ) then
         pos_exp = index( str, "e", back=.true. )
      endif
      if ( pos_exp > 0 ) then
         call str2dec( str(pos_exp+1:lens), n_exp ) 
      else
         pos_exp = lens + 1
      endif
      call str2dec( str(1:pos_exp-1), n, expnt )
      r = real( n, kind=dp ) * ( ten**(n_exp + expnt) ) 
      return
   end subroutine
!
   elemental &
   subroutine str2dec( s, n, expnt )
      character(len=*), intent(in) :: s
      integer(i8), intent(out) :: n 
      integer, intent(out), optional :: expnt
      integer :: ipos, ic, pos_period, iexponent
!
      n = 0_i8
      pos_period = 0
      iexponent = -1
      if ( present(expnt) ) iexponent = 0 
      do ipos = len(s), 1, -1
         ic = ichar( s(ipos:ipos) )
         if ( present(expnt) ) then 
            if ( ic == ascii_period ) then
               pos_period = ipos
               cycle
            endif
         endif 
         if ( ic == ascii_negative ) then
            n = -n
            exit
         endif 
         if ( (ic < ascii_0) .or. (ic > ascii_9) ) exit
         iexponent = iexponent + 1 
         n = n + (ten_int**iexponent)*(ic - 48)
      enddo
      if ( present(expnt) ) expnt = -len(s) + pos_period - 1
      return
   end subroutine 
!------------------------------------------------------------------------------
!
   elemental &
   function str2real ( s ) result ( r )
!
      character(*), target, intent(in) :: s
      real(dp) :: r
!
      integer, parameter :: N = 32
      real(dp), parameter :: expo(N) = &
         [1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0, &
          1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, &
          1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, &
          1e-17, 1e-18, 0., 0., 0., 0.]
      character(N) :: equ_s
      integer*1 :: equ_i(N)
      integer*1 :: mask(N)

      equivalence(equ_i, equ_s)

      equ_s = s
      equ_i = equ_i - 48

      where (0 <= equ_i .and. equ_i <= 9)
         mask = 1
      elsewhere
         mask = 0
      end where

      equ_i = equ_i * mask
      r = sum(equ_i * expo) * 10**(equ_i(29)*10+equ_i(30))
!
      return 
   end function str2real
!------------------------------------------------------------------------------
   end module
!------------------------------------------------------------------------------

```

```auto
!======================================================================
!
   program main 
!
   use, intrinsic :: iso_fortran_env, only: dp => real64, i8 => int64
   use mod_st_to_dp
!
   implicit none 
!  
   integer,parameter :: n = 1000000 !! number of values 
!  
   character(len=30),dimension(:),allocatable :: strs
   real(dp),dimension(:),allocatable :: rval, rref 
!
   integer :: i, i0 
   integer :: ierr
   real(dp) :: r
   integer(i8) :: start, finish, count_rate
   real(dp),parameter :: eps = epsilon(1.0_dp) 
!
!
! create a list of values to parse
!
   allocate( strs(n), rval(n), rref(n) )
!
! Initializing RNG: with a dummy i
   r = util_unirand (i)

   i0 = int(n*r) + 1 

   write(*,*) 'N =', n, ', i0 =', i0

   do i = 1, n
      rref(i) = util_unirand()
   enddo

   call system_clock(start, count_rate)
   do i = 1, n
      write(strs(i), '(E30.16)') rref(i)
   enddo
   call system_clock(finish)
   write(*,'(A30,1X,F7.4,1X,A)') 'Write: time consumed =', &
      (finish-start)/real(count_rate,dp), ' seconds'

   
   blk_io: block
      write(*,11) "BLOCK 1: formatted read toward string to double"
!
      call system_clock(start, count_rate)
      do i = 1, n
         read(strs(i),fmt=*,iostat=ierr) rval(i)
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during formatted read."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_io
!! endif 
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_io

   blk_c: block
      write(*,11) "BLOCK 2: C st_to_r8"
!
      call system_clock(start, count_rate)
      do i = 1, n
         call c_st_to_r8 ( strs(i), rval(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during st_to_r8."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_c
!! endif 

!
      call system_clock(start, count_rate)
      call c_st_to_r8 ( strs, rval )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_c

   blk_f: block
      write(*,11) "BLOCK 3: F st_to_r8"
!
      call system_clock (start, count_rate)
      do i = 1, n
         call f_st_to_r8 ( strs(i), rval(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during st_to_r8."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_f
!! endif 
!
      call system_clock(start, count_rate)
      call f_st_to_r8 ( strs, rval )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_f

   blk_4: block
      write(*,11) "BLOCK 4: F str2real "
!
      call system_clock (start, count_rate)
      do i = 1, n
         rval(i) = str2real ( strs(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
      call system_clock(start, count_rate)
      rval = str2real ( strs )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_4

!
10 format ( 'i =', i9, ', string(i) =', a, ', zval(i) =', e25.17 )
11 format ( /, 80('-'), /, a )
!
   deallocate ( strs, rval, rref ) 
!
   STOP 
!
   contains 
!======================================================================
!
! This is the ran0 in Numerical Rrecipes in Fortran.
! To initialize, 
! call system_clock( idummy )
! Do not alter idummy between successive deviates.
!
      function util_unirand ( idum ) result( ran0 )
!
      implicit none 
      integer,parameter :: rk = kind(1.0D0), ik = 4
!
! ARGUMENTS:
!
      real(rk) :: ran0
      integer(ik),intent(in),optional :: idum 
!
! local constants and variable
!
      integer(ik) :: k
      integer(ik),save :: idummy 
      integer(ik),parameter :: &
         ia=16807, im=2147483647, iq=127773, ir=2836, mask=123459876
      real(rk),parameter :: am = 1.0_rk / im
!
!
      if ( present(idum) ) call system_clock( idummy )
!
      idummy = ieor( idummy, mask )
      k = idummy / iq
      idummy = ia*( idummy - k*iq ) - ir*k
      if ( idummy .lt. 0 ) idummy = idummy + im
      ran0 = am*idummy
      idummy = ieor( idummy, mask )
      return
      end function
!
!=====
      subroutine show_err ( er, ep ) 
      real(dp),intent(in) :: er, ep 
      real(dp) :: tmp 
!
      tmp = ep - er
      write(*,10) er, ep, tmp 
      if ( tmp .lt. 0.0_dp ) write(*,11) -tmp/ep 
      return 
 10 format( 'A = Max |rval(:)-rref(:)| =', 1pe10.3,',', 3x, &
              'B = epsilon(1.0d0) =', 1pe10.3,',', 3x, &
              'B-A =', 1pe11.3 )   
 11 format( '*** WARNING: A > B, (A-B)/epsilon =', 1pe11.3 )
      end subroutine 
!
!======================================================================
   end program 
!======================================================================

```

**Conclusion:** (obsolete)

- direct method: slow
- strtod (namely c\_st\_to\_r8): fast
- f\_strtod (namely f\_st\_to\_r8): fastest. But the accuracy may be lost, and this issue depends on compiler. For instance, the error is about \<= 7.5\*epsilon with Gfortran, while with Ifort the error is \<= epsilon.
- str2real: does not work.

==============================================================

**UPDATE 1**

Based on [Carltoffel](https://fortran-lang.discourse.group/u/Carltoffel)’s comment, I can check his current version only with Ifort. My gfortran can not compile the code since it needs to be upgraded to the 9.x versions.

**Results (with Ifort)**

> N = 1000000 , i0 = 84194  
> Write: time consumed = 0.3079 seconds
> 
> * * *
> 
> BLOCK 1: formatted read toward string to double  
> Read: time consumed = 0.3865 seconds  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1036 seconds  
> Read: time consumed = 0.1049 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0943 seconds  
> Read: time consumed = 0.0970 seconds (array)  
> A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00
> 
> * * *
> 
> BLOCK 4: F str2real  
> Read: time consumed = 0.0609 seconds  
> Read: time consumed = 0.0605 seconds (array)  
> A = Max |rval(:)-rref(:)| = 3.331E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.110E-16  
> \*\*\* WARNING: A \> B, A/B = 1.500E+00

**Conclusion:** (only from my side)

- direct method: slow
- strtod (namely c\_st\_to\_r8): fast
- f\_strtod (namely f\_st\_to\_r8): faster.
- str2real: fastest. But the accuracy may be lost, and this issue may depend only on compiler. For instance, with Ifort the error is about 1.5\*epsilon.

Source code of [str2real\_m.f90](https://github.com/Carltoffel/Fortran-String-to-Real/blob/main/src/str2real_m.f90), in which I added the elemental attribution to call the routine with array, as in the test program. To use the new **str2real** , we must add the line **use str2real\_m** to the test program.

```auto
module str2real_m
implicit none
private
public :: str2real

integer, parameter :: wp = kind(1.0d0)
integer, parameter :: ikind = selected_int_kind(2)
integer(kind=ikind), parameter :: digit_0 = ichar('0')
integer(kind=ikind), parameter :: period = ichar('.') - digit_0
integer(kind=ikind), parameter :: minus_sign = ichar('-') - digit_0

real(wp), parameter :: whole_number_base(*) = &
        [1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, &
         1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0]
real(wp), parameter :: fractional_base(*) = &
        [1d-1, 1d-2, 1d-3, 1d-4, 1d-5, 1d-6, 1d-7, 1d-8, &
         1d-9, 1d-10, 1d-11, 1d-12, 1d-13, 1d-14, 1d-15, 1d-16, &
         1d-17, 1d-18, 1d-19, 1d-20, 1d-21, 1d-22, 1d-23, 1d-24]
real(wp), parameter :: period_skip = 0d0
real(wp), parameter :: base(*) = &
        [whole_number_base, period_skip, fractional_base]

contains

elemental &
function str2real(s) result(r)
character(*), intent(in) :: s
real(wp) :: r

real(wp) :: r_coefficient
real(wp) :: r_exponent

integer, parameter :: N = 32
character(N) :: factors_char
integer(kind=ikind) :: factors(N)
integer(kind=ikind) :: mask(N)
integer :: period_loc
integer :: exponent_loc
integer :: mask_from
integer :: mask_till
intrinsic :: findloc, scan
integer :: findloc, scan

equivalence(factors, factors_char)
factors_char = s
factors = factors - digit_0

period_loc = findloc(factors, period, 1)
exponent_loc = scan(s, 'eE', back=.true.)
if(exponent_loc == 0) exponent_loc = len(s) + 1
if(period_loc == 0) period_loc = exponent_loc

where (0 <= factors .and. factors <= 9)
    mask = 1
elsewhere
    mask = 0
end where

mask_from = 18 - period_loc
mask_till = mask_from + exponent_loc - 2

r_coefficient = sum( &
        factors(:exponent_loc - 1) * &
        base(mask_from:mask_till) * &
        mask(:exponent_loc - 1))
r_exponent = sum( &
        factors(exponent_loc+1:len(s)) * &
        mask(exponent_loc+1:len(s)) * &
        base(17-(len(s)-exponent_loc):16))
if(factors(exponent_loc+1) == minus_sign) r_exponent = -r_exponent
if(factors(1) == minus_sign) r_coefficient = -r_coefficient
r = r_coefficient * 10 ** r_exponent
end function str2real

end module mod_str2real

```

**Test program:**

```auto
   program main 
!
   use, intrinsic :: iso_fortran_env, only: dp => real64, i8 => int64
   use mod_st_to_dp
   use str2real_m
!
   implicit none 
!  
   integer,parameter :: n = 1000000 !! number of values 
!  
   character(len=30),dimension(:),allocatable :: strs
   real(dp),dimension(:),allocatable :: rval, rref 
!
   integer :: i, i0 
   integer :: ierr
   real(dp) :: r
   integer(i8) :: start, finish, count_rate
   real(dp),parameter :: eps = epsilon(1.0_dp) 
!
!
! create a list of values to parse
!
   allocate( strs(n), rval(n), rref(n) )
!
! Initializing RNG: with a dummy i
   r = util_unirand (i)

   i0 = int(n*r) + 1 

   write(*,*) 'N =', n, ', i0 =', i0

   do i = 1, n
      rref(i) = util_unirand()
   enddo

   call system_clock(start, count_rate)
   do i = 1, n
      write(strs(i), '(E30.16)') rref(i)
   enddo
   call system_clock(finish)
   write(*,'(A30,1X,F7.4,1X,A)') 'Write: time consumed =', &
      (finish-start)/real(count_rate,dp), ' seconds'

   
   blk_io: block
      write(*,11) "BLOCK 1: formatted read toward string to double"
!
      call system_clock(start, count_rate)
      do i = 1, n
         read(strs(i),fmt=*,iostat=ierr) rval(i)
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during formatted read."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_io
!! endif 
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_io

   blk_c: block
      write(*,11) "BLOCK 2: C st_to_r8"
!
      call system_clock(start, count_rate)
      do i = 1, n
         call c_st_to_r8 ( strs(i), rval(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during st_to_r8."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_c
!! endif 

!
      call system_clock(start, count_rate)
      call c_st_to_r8 ( strs, rval )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_c

   blk_f: block
      write(*,11) "BLOCK 3: F st_to_r8"
!
      call system_clock (start, count_rate)
      do i = 1, n
         call f_st_to_r8 ( strs(i), rval(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during st_to_r8."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_f
!! endif 
!
      call system_clock(start, count_rate)
      call f_st_to_r8 ( strs, rval )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_f

   blk_4: block
      write(*,11) "BLOCK 4: F str2real "
!
      call system_clock (start, count_rate)
      do i = 1, n
         rval(i) = str2real ( strs(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds'
!
      call system_clock(start, count_rate)
      rval = str2real ( strs )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_4

!
10 format ( 'i =', i9, ', string(i) =', a, ', zval(i) =', e25.17 )
11 format ( /, 80('-'), /, a )
!
   deallocate ( strs, rval, rref ) 
!
   STOP 
!
   contains 
!======================================================================
!
! This is the ran0 in Numerical Rrecipes in Fortran.
! To initialize, 
! call system_clock( idummy )
! Do not alter idummy between successive deviates.
!
      function util_unirand ( idum ) result( ran0 )
!
      implicit none 
      integer,parameter :: rk = kind(1.0D0), ik = 4
!
! ARGUMENTS:
!
      real(rk) :: ran0
      integer(ik),intent(in),optional :: idum 
!
! local constants and variable
!
      integer(ik) :: k
      integer(ik),save :: idummy 
      integer(ik),parameter :: &
         ia=16807, im=2147483647, iq=127773, ir=2836, mask=123459876
      real(rk),parameter :: am = 1.0_rk / im
!
!
      if ( present(idum) ) call system_clock( idummy )
!
      idummy = ieor( idummy, mask )
      k = idummy / iq
      idummy = ia*( idummy - k*iq ) - ir*k
      if ( idummy .lt. 0 ) idummy = idummy + im
      ran0 = am*idummy
      idummy = ieor( idummy, mask )
      return
      end function
!
!=====
      subroutine show_err ( er, ep ) 
      real(dp),intent(in) :: er, ep 
      real(dp) :: tmp 
!
      tmp = ep - er
      write(*,10) er, ep, tmp 
      if ( tmp .lt. 0.0_dp ) write(*,11) er/ep 
      return 
 10 format( 'A = Max |rval(:)-rref(:)| =', 1pe10.3,',', 3x, &
              'B = epsilon(1.0d0) =', 1pe10.3,',', 3x, &
              'B-A =', 1pe11.3 )   
 11 format( '*** WARNING: A > B, A/B =', 1pe11.3 )
      end subroutine 
!
!======================================================================
   end program 
!======================================================================

```

---

<div class="post-metadata">

**Author:** ![Carltoffel](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/carltoffel/32/1680_2.png) [@Carltoffel](https://fortran-lang.discourse.group/u/Carltoffel)\
**Post date:** [June 1, 2022, 5:47am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/55 "2022-06-01T05:47:56Z")

</div>

Welcome to the Fortran community!  
I think you are referring to the very first dummy version of `str2real`. Have you checked out the updated version from GitHub?

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [June 1, 2022, 5:56am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/56 "2022-06-01T05:56:46Z")

</div>

No. I will check your version in GitHub and let you know. Thank you.

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [June 1, 2022, 7:28am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/57 "2022-06-01T07:28:13Z")

</div>

To Carltoffel:

The problem to my gfortran compiler is the FINDLOC function in your codes. The compiler does not understand that function. So, I replace

```auto
   period_loc = findloc(factors, period, 1)

```

by

```auto
   do period_loc = 1,N
      if ( factors(period_loc) == period ) exit 
   enddo

```

Your code works. Here are the results from both Ifort and Gfortran.

**1. Gfortran**

> N = 1000000 , i0 = 378457  
> Write: time consumed = 0.7423 seconds
> 
> * * *
> 
> BLOCK 1: formatted read toward string to double  
> Read: time consumed = 0.6479 seconds  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1044 seconds  
> Read: time consumed = 0.1035 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0964 seconds  
> Read: time consumed = 0.0979 seconds (array)  
> A = Max |rval(:)-rref(:)| = 1.887E-15, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.665E-15  
> \*\*\* WARNING: A \> B, A/B = 8.500E+00
> 
> * * *
> 
> BLOCK 4: F str2real  
> Read: time consumed = 0.0580 seconds  
> Read: time consumed = 0.0603 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00

**2. Ifort**

> N = 1000000 , i0 = 284882  
> Write: time consumed = 0.3148 seconds
> 
> * * *
> 
> BLOCK 1: formatted read toward string to double  
> Read: time consumed = 0.4024 seconds  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1043 seconds  
> Read: time consumed = 0.1036 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0871 seconds  
> Read: time consumed = 0.0865 seconds (array)  
> A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00
> 
> * * *
> 
> BLOCK 4: F str2real  
> Read: time consumed = 0.0589 seconds  
> Read: time consumed = 0.0584 seconds (array)  
> A = Max |rval(:)-rref(:)| = 3.331E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.110E-16  
> \*\*\* WARNING: A \> B, A/B = 1.500E+00

Regarding your code, result from Gfortran is less accurate than the one from Ifort. However,  
your code runs faster (with both compilers) and performs more accurate than f\_strtod (with Gfortran). It may depend on compilers and on how we compile codes. In my opinion, all are acceptable. Thank you for sharing your amazing code.

---

<div class="post-metadata">

**Author:** ![Carltoffel](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/carltoffel/32/1680_2.png) [@Carltoffel](https://fortran-lang.discourse.group/u/Carltoffel)\
**Post date:** [June 1, 2022, 7:50am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/58 "2022-06-01T07:50:10Z")

</div>

Thank you for your effort to test all these routines!  
I think the accuracy is lost because of the vector expression that sums the digits. Maybe I can find a way to increase the accuracy without losing much performance.

Which compiler flags did you use? `-Ofast`? `-march=native`?

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [June 1, 2022, 8:04am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/59 "2022-06-01T08:04:25Z")

</div>

Compiling with Gfortran, I use many flags:

> -O3 -fstack-arrays -static -finit-local-zero -Wall -Wextra -Waliasing -Wconversion -pedantic

The flag “-O3” may be the main role.

With Ifort, I use:

> -O3 -ipo -fno-alias -zero -warn unused -warn uncalled -static

Again, the flag “-O3” is responsible.

I haven’t tried your suggestions yet. Thank you.

---

<div class="post-metadata">

**Author:** ![hkvzjal](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/hkvzjal/32/3055_2.png) [@hkvzjal](https://fortran-lang.discourse.group/u/hkvzjal)\
**Post date:** [January 7, 2023, 11:23pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/60 "2023-01-07T23:23:52Z")

</div>

Hi! I got really inspired by this thread and it helped me boost reading huge mesh files!! since this helped me so much I wanted to share my grain of salt in case it can be useful:

```auto
module str2real_m
implicit none
private
public :: str2real

integer, parameter :: wp = kind(1.0d0)
integer, parameter :: ikind = selected_int_kind(2)
integer(kind=ikind), parameter :: digit_0 = ichar('0')
integer(kind=ikind), parameter :: period = ichar('.') - digit_0
integer(kind=ikind), parameter :: minus_sign = ichar('-') - digit_0

real(wp), parameter :: whole_number_base(*) = &
        [1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, &
         1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0]
real(wp), parameter :: fractional_base(*) = &
        [1d-1, 1d-2, 1d-3, 1d-4, 1d-5, 1d-6, 1d-7, 1d-8, &
         1d-9, 1d-10, 1d-11, 1d-12, 1d-13, 1d-14, 1d-15, 1d-16, &
         1d-17, 1d-18, 1d-19, 1d-20, 1d-21, 1d-22, 1d-23, 1d-24]
real(wp), parameter :: period_skip = 0d0
real(wp), parameter :: base(*) = &
        [whole_number_base, period_skip, fractional_base]
real(wp), parameter :: expbase(*) = [whole_number_base, fractional_base]

contains

elemental function str2real(s) result(r)
    character(*), intent(in) :: s
    real(wp) :: r

    real(wp) :: r_coefficient

    integer, parameter :: N = 32
    character(N) :: factors_char
    integer(kind=ikind) :: factors(N)
    integer(kind=ikind) :: mask(N)
    integer :: period_loc
    integer :: exponent_loc
    integer :: exponent_end
    integer :: mask_from
    integer :: mask_till
    intrinsic :: findloc, scan
    integer :: findloc, scan
    integer(1) :: sig_coef, sig_exp
    integer :: i_exponent
    
    equivalence(factors, factors_char)
    sig_coef = 1; sig_exp = 1
    factors_char = s
    factors = factors - digit_0
    
    period_loc = findloc(factors, period, 1)
    exponent_loc = scan(s, 'eE', back=.true.)
    if(exponent_loc == 0) exponent_loc = len(s) + 1
    if(period_loc == 0) period_loc = exponent_loc

    where (0 <= factors .and. factors <= 9)
        mask = 1
    elsewhere
        mask = 0
    end where
    
    if(factors(exponent_loc+1) == minus_sign) sig_exp = -1
    if(factors(1)== minus_sign) sig_coef = -1
    
    mask_from = 18 - period_loc
    mask_till = mask_from + exponent_loc - 2

    r_coefficient = sum( &
            factors(:exponent_loc - 1) * &
            base(mask_from:mask_till) * &
            mask(:exponent_loc - 1))
    
    i_exponent = str2int( s(exponent_loc+1:len(s)) )
    r = (sig_coef*r_coefficient) * expbase(16-sig_exp*i_exponent)
end function

elemental function str2int(s) result(int)
    character(*), intent(in) :: s
    integer :: int
    integer :: i, val
    
    int = 0
    do i = 1, len(s)
        val = iachar(s(i:i))-digit_0
        if( val >= 0 .and. val <= 9 ) int = int*10 + val
    end do
    
end function

end module str2real_m

```

There are a few changes with different purposes:

1. in order to avoid the problem with white spaces or line endings (CR LF) being mixed in the string I replaced reading the exponent with the str2int function (this was also very useful for reading large arrays of integers).

2. In order to accelerate a bit more the computation:  
2.1) Instead of computing the exponential as a sum I took directly the table of bases and constructed the table expbase(\*) = [whole\_number\_base, fractional\_base] which gives the value to multiply the coefficient.  
2.2) I stored the signs for the coefficient and exponential in two integer(1) values.

With these changes:

I used the test in the GitHub and added the following check:

```auto
call check(" 2.5647869e-003 "//char(13)//char(10)) 

```

There is one white space before and after the string plus CRLF at the end! this passes without problem

I also ran the tests by @tqviet ( working in Windows11 / Intel 19 / -O3 )

Before the changes I had for the Block 4: time consumed = 0.086 (both lines)  
And after: time consumed = 0.073 for the loop, and 0.07 for the array

**Results**

```auto
--------------------------------------------------------------------------------
BLOCK 1: formatted read toward string to double
         Read: time consumed = 0.6290 seconds
A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16

--------------------------------------------------------------------------------
BLOCK 2: C st_to_r8
         Read: time consumed = 0.2350 seconds
         Read: time consumed = 0.2320 seconds (array)
A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16

--------------------------------------------------------------------------------
BLOCK 3: F st_to_r8
         Read: time consumed = 0.1130 seconds
         Read: time consumed = 0.1140 seconds (array)
A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00

--------------------------------------------------------------------------------
BLOCK 4: F str2real
         Read: time consumed = 0.0730 seconds
         Read: time consumed = 0.0700 seconds (array)
A = Max |rval(:)-rref(:)| = 3.331E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.110E-16
*** WARNING: A > B, A/B = 1.500E+00

```

==============================================================  
**UPDATE**

I tried a “simplification”:

```auto
elemental function str2real(s) result(r)
    character(*), intent(in) :: s
    real(wp) :: r
    real(wp) :: r_fraction
    integer, parameter :: N = 32
    character(N) :: factors_char
    integer(kind=ikind) :: factors(N)
    integer :: period_loc
    integer :: exponent_loc
    intrinsic :: findloc, scan
    integer :: findloc, scan
    integer(1) :: sig_coef, sig_exp
    integer :: i_exponent, i, val
    
    equivalence(factors, factors_char)
    sig_coef = 1; sig_exp = 1
    factors_char = s
    factors = factors - digit_0
    
    period_loc = findloc(factors, period, 1)
    exponent_loc = scan(s, 'eE', back=.true.)
    if(exponent_loc == 0) exponent_loc = len(s) + 1
    if(period_loc == 0) period_loc = exponent_loc
    
    if(factors(exponent_loc+1) == minus_sign) sig_exp = -1
    if(factors(1)== minus_sign) sig_coef = -1
            
    val = str2int( s(1:period_loc-1) )
    r = val
    do i = period_loc+1, exponent_loc-1
        val = factors(i)
        if( val >= 0 .and. val <= 9 ) r = r + val*fractional_base(i-period_loc)
    end do
    r = sig_coef*r
    
    i_exponent = str2int( s(exponent_loc+1:len(s)) )
    if(i_exponent.ne.0) r = r * expbase(16-sig_exp*i_exponent)
end function

```

By computing the whole number part with the str2int function and then iterating on the fractional part plus avoiding multiplying by the exponential coefficient if not needed it brough my times down from 0.07s to 0.052s

**Results**

```auto
--------------------------------------------------------------------------------
BLOCK 1: formatted read toward string to double
         Read: time consumed = 0.6270 seconds
A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16

--------------------------------------------------------------------------------
BLOCK 2: C st_to_r8
         Read: time consumed = 0.2370 seconds
         Read: time consumed = 0.2320 seconds (array)
A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16

--------------------------------------------------------------------------------
BLOCK 3: F st_to_r8
         Read: time consumed = 0.1390 seconds
         Read: time consumed = 0.1360 seconds (array)
A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00

--------------------------------------------------------------------------------
BLOCK 4: F str2real
         Read: time consumed = 0.0520 seconds
         Read: time consumed = 0.0520 seconds (array)
A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16
*** WARNING: A > B, A/B = 2.500E+00

```

==============================================================  
**UPDATE 2**  
Just the change w.r.t to previous update of the last lines of the str2real function:

```auto
    val = str2int( s(1:period_loc-1) )
    r = sum( factors(period_loc+1:exponent_loc-1)*fractional_base(1:exponent_loc-period_loc-1) )
    r = sig_coef*(r+val)
    
    i_exponent = str2int( s(exponent_loc+1:len(s)) )
    if(i_exponent.ne.0) r = r * expbase(16-sig_exp*i_exponent)

```

**Results**

```auto
--------------------------------------------------------------------------------
BLOCK 1: formatted read toward string to double
         Read: time consumed = 0.6390 seconds
A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16

--------------------------------------------------------------------------------
BLOCK 2: C st_to_r8
         Read: time consumed = 0.2370 seconds
         Read: time consumed = 0.2350 seconds (array)
A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16

--------------------------------------------------------------------------------
BLOCK 3: F st_to_r8
         Read: time consumed = 0.1160 seconds
         Read: time consumed = 0.1140 seconds (array)
A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00

--------------------------------------------------------------------------------
BLOCK 4: F str2real
         Read: time consumed = 0.0480 seconds
         Read: time consumed = 0.0480 seconds (array)
A = Max |rval(:)-rref(:)| = 2.220E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = 0.000E+00

```

My time went down now to 0.048 and the error is now B-A=0

By the way @Carltoffel I think in line 41 of your test.f90 file the test should be:

```auto
if(abs(rel_err) > epsilon(1.0d0)) then

```

Otherwise I was getting a print with call check(“0.1234E0”), when both values were identical to the last decimal but “abs\_err = str2real\_out - formatted\_read\_out” was returning a value smaller than the precision

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [January 10, 2023, 7:06am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/61 "2023-01-10T07:06:49Z")

</div>

Thank you @ [hkvzjal](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/60) for your sharing. After checking your codes and comparing them with the previous versions, I conclude that your version with **UPDATE 2** runs fastest, while your other versions also show very good performance. It is very interesting!

Herein I’d like to post all modules and the testing program, as follows:

1. Your versions:

```auto
      module mod_str2real_02
!      
      implicit none
      private
!      
      public :: str2real_02
      public :: str2real_03
      public :: str2real_04
!      
      integer, parameter :: wp = kind(1.0d0)
      integer, parameter :: ikind = selected_int_kind(2)
      integer(kind=ikind), parameter :: digit_0 = ichar('0')
      integer(kind=ikind), parameter :: period = ichar('.')-digit_0
      integer(kind=ikind), parameter :: minus_sign = ichar('-')-digit_0
!      
      real(wp), parameter :: whole_number_base(*) = &
              [1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, &
               1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0]
!      
      real(wp), parameter :: fractional_base(*) = &
              [1d-1, 1d-2, 1d-3, 1d-4, 1d-5, 1d-6, 1d-7, 1d-8, &
               1d-9, 1d-10, 1d-11, 1d-12, 1d-13, 1d-14, 1d-15, 1d-16, &
               1d-17, 1d-18, 1d-19, 1d-20, 1d-21, 1d-22, 1d-23, 1d-24]
!      
      real(wp), parameter :: period_skip = 0d0
      real(wp), parameter :: base(*) = &
         [whole_number_base, period_skip, fractional_base]
      real(wp), parameter :: expbase(*) = &
         [whole_number_base, fractional_base]
!     
!------------------------------------------------------------------------------  
      contains
!------------------------------------------------------------------------------  
! hkvzjal - version 01 
!
      elemental &
      function str2real_02(s) result(r)
      character(*), intent(in) :: s
      real(wp) :: r
      real(wp) :: r_coefficient
      integer, parameter :: N = 32
      character(N) :: factors_char
      integer(kind=ikind) :: factors(N)
      integer(kind=ikind) :: mask(N)
      integer :: period_loc
      integer :: exponent_loc
      integer :: exponent_end
      integer :: mask_from
      integer :: mask_till
!! intrinsic :: findloc
!! integer :: findloc
      intrinsic :: scan
      integer :: scan
      integer(1) :: sig_coef, sig_exp
      integer :: i_exponent
!
!
      equivalence(factors, factors_char)
!
      sig_coef = 1; sig_exp = 1
      factors_char = s
      factors = factors - digit_0
!
!! period_loc = findloc(factors, period, 1)
      do period_loc = 1,N
         if ( factors(period_loc) == period ) exit 
      enddo
!
      exponent_loc = scan(s, 'eE', back=.true.)
!
      if(exponent_loc == 0) exponent_loc = len(s) + 1
      if(period_loc == 0) period_loc = exponent_loc
!
      where (0 <= factors .and. factors <= 9)
         mask = 1
      elsewhere
         mask = 0
      end where
!      
      if(factors(exponent_loc+1) == minus_sign) sig_exp = -1
      if(factors(1)== minus_sign) sig_coef = -1
!      
      mask_from = 18 - period_loc
      mask_till = mask_from + exponent_loc - 2
!      
      r_coefficient = sum( &
              factors(:exponent_loc - 1) * &
              base(mask_from:mask_till) * &
              mask(:exponent_loc - 1))
!  
      i_exponent = str2int( s(exponent_loc+1:len(s)) )
      r = (sig_coef*r_coefficient) * expbase(16-sig_exp*i_exponent)
!
      end function
!------------------------------------------------------------------------------  
      elemental &
      function str2int(s) result(iou)
      character(*), intent(in) :: s
      integer :: iou
      integer :: i, val
      iou = 0
      do i = 1, len(s)
         val = iachar(s(i:i))-digit_0
         if( val >= 0 .and. val <= 9 ) iou = iou*10 + val
      end do
      end function
!------------------------------------------------------------------------------  
! hkvzjal - version 02 
!
      elemental &
      function str2real_03(s) result(r)
      character(*), intent(in) :: s
      real(wp) :: r
      real(wp) :: r_fraction
      integer, parameter :: N = 32
      character(N) :: factors_char
      integer(kind=ikind) :: factors(N)
      integer :: period_loc
      integer :: exponent_loc
!! intrinsic :: findloc
!! integer :: findloc
      intrinsic :: scan
      integer :: scan
      integer(1) :: sig_coef, sig_exp
      integer :: i_exponent, i, val
!      
      equivalence(factors, factors_char)
!      
      sig_coef = 1; sig_exp = 1
      factors_char = s
      factors = factors - digit_0
!
!! period_loc = findloc(factors, period, 1)
      do period_loc = 1,N
         if ( factors(period_loc) == period ) exit 
      enddo
!
      exponent_loc = scan(s, 'eE', back=.true.)
      if (exponent_loc == 0) exponent_loc = len(s) + 1
      if (period_loc == 0) period_loc = exponent_loc
      if (factors(exponent_loc+1) == minus_sign) sig_exp = -1
      if (factors(1)== minus_sign) sig_coef = -1
      val = str2int( s(1:period_loc-1) )
      r = val
      do i = period_loc+1, exponent_loc-1
         val = factors(i)
         if( val >= 0 .and. val <= 9 ) &
            r = r + val*fractional_base(i-period_loc) 
      end do
      r = sig_coef*r
      i_exponent = str2int( s(exponent_loc+1:len(s)) )
      if(i_exponent.ne.0) r = r * expbase(16-sig_exp*i_exponent)
!
      end function
!------------------------------------------------------------------------------  
! hkvzjal - version 03 
!
      elemental &
      function str2real_04 (s) result(r)
      character(*), intent(in) :: s
      real(wp) :: r
      real(wp) :: r_fraction
      integer, parameter :: N = 32
      character(N) :: factors_char
      integer(kind=ikind) :: factors(N)
      integer :: period_loc
      integer :: exponent_loc
!! intrinsic :: findloc
!! integer :: findloc
      intrinsic :: scan
      integer :: scan
      integer(1) :: sig_coef, sig_exp
      integer :: i_exponent, i, val
!      
      equivalence(factors, factors_char)
!      
      sig_coef = 1; sig_exp = 1
      factors_char = s
      factors = factors - digit_0
!
!! period_loc = findloc(factors, period, 1)
      do period_loc = 1,N
         if ( factors(period_loc) == period ) exit 
      enddo
!
      exponent_loc = scan(s, 'eE', back=.true.)
      if (exponent_loc == 0) exponent_loc = len(s) + 1
      if (period_loc == 0) period_loc = exponent_loc
      if (factors(exponent_loc+1) == minus_sign) sig_exp = -1
      if (factors(1)== minus_sign) sig_coef = -1
      val = str2int( s(1:period_loc-1) )
      r = sum( factors(period_loc+1:exponent_loc-1) * &
               fractional_base(1:exponent_loc-period_loc-1) )
      r = sig_coef*(r+val)
      i_exponent = str2int( s(exponent_loc+1:len(s)) )
      if(i_exponent.ne.0) r = r * expbase(16-sig_exp*i_exponent)
!
      end function  
!------------------------------------------------------------------------------  
      end module mod_str2real_02
!------------------------------------------------------------------------------

```

1. The previous version:

```auto
module mod_str2real
!
   implicit none
   private
!
   public :: str2real
!  
   integer, parameter :: wp = kind(1.0d0)
   integer, parameter :: ikind = selected_int_kind(2)
!
   integer(kind=ikind), parameter :: digit_0 = ichar('0')
   integer(kind=ikind), parameter :: period = ichar('.') - digit_0
   integer(kind=ikind), parameter :: minus_sign = ichar('-') - digit_0
!   
   real(wp), parameter :: whole_number_base(*) = &
           [1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, &
            1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0]
   real(wp), parameter :: period_skip = 0d0
   real(wp), parameter :: fractional_base(*) = &
           [1d-1, 1d-2, 1d-3, 1d-4, 1d-5, 1d-6, 1d-7, 1d-8, &
            1d-9, 1d-10, 1d-11, 1d-12, 1d-13, 1d-14, 1d-15, 1d-16, &
            1d-17, 1d-18, 1d-19, 1d-20, 1d-21, 1d-22, 1d-23, 1d-24]
   real(wp), parameter :: base(*) = &
           [whole_number_base, period_skip, fractional_base]
!------------------------------------------------------------------------------   
   contains
!------------------------------------------------------------------------------   
   elemental &
   function str2real(s) result(r)
   character(*), intent(in) :: s
   real(wp) :: r
!   
   real(wp) :: r_coefficient
   real(wp) :: r_exponent
!
! integer, parameter :: r10 = selected_real_kind ( P=16 ) !<-- not more accurate
! real(r10) :: r_coefficient
! real(r10) :: r_exponent
!   
   integer, parameter :: N = 32
   character(N) :: factors_char
   integer(kind=ikind) :: factors(N)
   integer(kind=ikind) :: mask(N)
   integer :: period_loc
   integer :: exponent_loc
   integer :: mask_from
   integer :: mask_till
! intrinsic :: findloc
! integer :: findloc
   intrinsic :: scan
   integer :: scan
! integer :: i 
   
   equivalence(factors, factors_char)

   factors_char = s
   factors = factors - digit_0
   
!! period_loc = findloc(factors, period, 1)
   do period_loc = 1,N
      if ( factors(period_loc) == period ) exit 
   enddo
   
   exponent_loc = scan(s, 'eE', back=.true.)
   if(exponent_loc == 0) exponent_loc = len(s) + 1
   if(period_loc == 0) period_loc = exponent_loc
   
   where (0 <= factors .and. factors <= 9)
      mask = 1
   elsewhere
      mask = 0
   end where
!   
   mask_from = 18 - period_loc
   mask_till = mask_from + exponent_loc - 2
!   
   r_coefficient = sum( &
           factors(:exponent_loc - 1) * &
           base(mask_from:mask_till) * &
           mask(:exponent_loc - 1) )
   r_exponent = sum( &
           factors(exponent_loc+1:len(s)) * &
           mask(exponent_loc+1:len(s)) * &
           base(17-(len(s)-exponent_loc):16) )
   if ( factors(exponent_loc+1) == minus_sign ) r_exponent = -r_exponent
   if ( factors(1) == minus_sign ) r_coefficient = -r_coefficient
   r = r_coefficient * (10**r_exponent)
!
   end function str2real
!------------------------------------------------------------------------------   
end module mod_str2real

```

and

```auto
   module mod_st_to_dp
!
! Fortran 2003:
   use, intrinsic :: iso_c_binding, only: c_double, c_char, c_ptr, &
                                            c_null_ptr, c_long
! 
! Fortran 2008:
   use, intrinsic :: iso_fortran_env, only: dp => real64, i8 => int64
!
   implicit none 
!
   private
! 
   public :: f_st_to_r8
   public :: c_st_to_r8
!
!
   interface
      pure & 
      function c_strtod ( str, endptr ) result(d) bind(C, name="strtod")
!
!! <stdlib.h> :: double strtod(const char *str, char **endptr)
!
         import :: c_char, c_ptr, c_double
!! Argument list
         character(kind=c_char,len=1), intent(in) :: str(*)
!! type(c_ptr), intent(inout) :: endptr !<- since we need pure attribution.
         type(c_ptr), intent(in) :: endptr
!! function result
         real(c_double) :: d 
      end function 
   end interface
!
!
! integer, parameter :: dp = selected_real_kind( p=12 )
! FP constants
   real(dp), parameter :: ten = 10.0_dp
! Integer constants
   integer, parameter :: ascii_negative = 45
   integer, parameter :: ascii_period = 46
   integer, parameter :: ascii_0 = 48
   integer, parameter :: ascii_9 = 57
   integer(i8), parameter :: ten_int = 10_i8
!
   type(c_ptr),parameter :: endptr = c_null_ptr
!
!------------------------------------------------------------------------------
   contains
!------------------------------------------------------------------------------
   elemental &
   subroutine c_st_to_r8 ( str, r )
      character(len=*), intent(in) :: str
      real(dp), intent(out) :: r
!
      r = c_strtod ( str, endptr )
      return 
   end subroutine 
!------------------------------------------------------------------------------
   elemental &
   subroutine f_st_to_r8 ( str, r )
      character(len=*), intent(in) :: str
      real(dp), intent(out) :: r
! local variables
      integer(i8) :: n, n_exp
      integer :: lens, pos_exp, expnt
!
      r = 0.0_dp
      n_exp = 0
      lens = len_trim( str )
      pos_exp = index( str, "E", back=.true. ) ! <-- find the capital 'E'
      if ( pos_exp == 0 ) then
         pos_exp = index( str, "e", back=.true. )
      endif
      if ( pos_exp > 0 ) then
         call str2dec( str(pos_exp+1:lens), n_exp ) 
      else
         pos_exp = lens + 1
      endif
      call str2dec( str(1:pos_exp-1), n, expnt )
      r = real( n, kind=dp ) * ( ten**(n_exp + expnt) ) 
      return
   end subroutine
!------------------------------------------------------------------------------  
   elemental &
   subroutine str2dec( s, n, expnt )
      character(len=*), intent(in) :: s
      integer(i8), intent(out) :: n 
      integer, intent(out), optional :: expnt
      integer :: ipos, ic, pos_period, iexponent
!
      n = 0_i8
      pos_period = 0
      iexponent = -1
      if ( present(expnt) ) iexponent = 0 
      do ipos = len(s), 1, -1
         ic = ichar( s(ipos:ipos) )
         if ( present(expnt) ) then 
            if ( ic == ascii_period ) then
               pos_period = ipos
               cycle
            endif
         endif 
         if ( ic == ascii_negative ) then
            n = -n
            exit
         endif 
         if ( (ic < ascii_0) .or. (ic > ascii_9) ) exit
         iexponent = iexponent + 1 
         n = n + (ten_int**iexponent)*(ic - 48)
      enddo
      if ( present(expnt) ) expnt = -len(s) + pos_period - 1
      return
   end subroutine 
!------------------------------------------------------------------------------
   end module
!------------------------------------------------------------------------------

```

1. The testing programing:

```auto
   program main 
!
   use, intrinsic :: iso_fortran_env, only: dp => real64, i8 => int64
   use mod_st_to_dp
   use mod_str2real
   use mod_str2real_02
!
   implicit none 
!  
   integer,parameter :: n = 1000000 !! number of values 
!  
   character(len=30),dimension(:),allocatable :: strs
   real(dp),dimension(:),allocatable :: rval, rref 
!
   integer :: i, i0 
   integer :: ierr
   real(dp) :: r
   integer(i8) :: start, finish, count_rate
   real(dp),parameter :: eps = epsilon(1.0_dp) 
   real(dp) :: dt(20)
!
!
! create a list of values to parse
!
   allocate( strs(n), rval(n), rref(n) )
!
! Initializing RNG: with a dummy i
   r = util_unirand (i)

   i0 = int(n*r) + 1 

   write(*,*) 'N =', n, ', i0 =', i0

   do i = 1, n
      rref(i) = util_unirand()
   enddo

   call system_clock(start, count_rate)
   do i = 1, n
      write(strs(i), '(E30.16)') rref(i)
   enddo
   call system_clock(finish)
   write(*,'(A30,1X,F7.4,1X,A)') 'Write: time consumed =', &
      (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
!   
   blk_io: block
!
      write(*,11) "BLOCK 1: Direct reading formatted string to double"
!
      call system_clock(start, count_rate)
      do i = 1, n
         read(strs(i),fmt=*,iostat=ierr) rval(i)
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(1) = (finish-start)/real(count_rate,dp)
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during formatted read."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_io
!! endif 
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
   end block blk_io
!
!
   blk_c: block
!
      write(*,11) "BLOCK 2: C st_to_r8"
!
      call system_clock(start, count_rate)
      do i = 1, n
         call c_st_to_r8 ( strs(i), rval(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(2) = (finish-start)/real(count_rate,dp)
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during st_to_r8."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_c
!! endif 
!
      call system_clock(start, count_rate)
      call c_st_to_r8 ( strs, rval )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
      write(*,'(/,"Time consumed: Direct / This (serial) = ", F7.4)') dt(1) / dt(2)
!
   end block blk_c
!
!
   blk_f: block
!
      write(*,11) "BLOCK 3: F st_to_r8"
!
      call system_clock (start, count_rate)
      do i = 1, n
         call f_st_to_r8 ( strs(i), rval(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(3) = (finish-start)/real(count_rate,dp)
!
!! if ( abs(rval(i0)-rref(i0)) > eps ) then
!! print *, "Warning: mismatch during st_to_r8."
!! write(*,10) i0, trim(strs(i0)), rval(i0)
! exit blk_f
!! endif 
!
      call system_clock(start, count_rate)
      call f_st_to_r8 ( strs, rval )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
      write(*,'(/,"Time consumed: Direct / This (serial) = ", F7.4)') dt(1) / dt(3)
!
   end block blk_f
!
!
   blk_4: block
!
      write(*,11) "BLOCK 4: F str2real (Carltoffel)"
!
      call system_clock (start, count_rate)
      do i = 1, n
         rval(i) = str2real ( strs(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(4) = (finish-start)/real(count_rate,dp)
!
      call system_clock(start, count_rate)
      rval = str2real ( strs )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
      write(*,'(/,"Time consumed: Direct / This (serial) = ", F7.4)') dt(1) / dt(4)
!
   end block blk_4

!
!
   blk_5: block
!
      write(*,11) "BLOCK 5: F str2real_02 (hkvzjal - version 01)"
!
      call system_clock (start, count_rate)
      do i = 1, n
         rval(i) = str2real_02 ( strs(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(5) = (finish-start)/real(count_rate,dp)
!
      call system_clock(start, count_rate)
      rval = str2real_02 ( strs )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
      write(*,'(/,"Time consumed: Direct / This (serial) = ", F7.4)') dt(1) / dt(5)
!
   end block blk_5

!
!
   blk_6: block
!
      write(*,11) "BLOCK 6: F str2real_03 (hkvzjal - version 02) "
!
      call system_clock (start, count_rate)
      do i = 1, n
         rval(i) = str2real_03 ( strs(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(6) = (finish-start)/real(count_rate,dp)
!
      call system_clock(start, count_rate)
      rval = str2real_03 ( strs )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
      write(*,'(/,"Time consumed: Direct / This (serial) = ", F7.4)') dt(1) / dt(6)
!
   end block blk_6

!
!
   blk_7: block
!
      write(*,11) "BLOCK 7: F str2real_04 (hkvzjal - version 03) "
!
      call system_clock (start, count_rate)
      do i = 1, n
         rval(i) = str2real_04 ( strs(i) )
      enddo
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (serial)'
!
      dt(7) = (finish-start)/real(count_rate,dp)
!
      call system_clock(start, count_rate)
      rval = str2real_04 ( strs )
      call system_clock(finish)
      write(*,'(A30,1X,F7.4,1X,A)') 'Read: time consumed =', &
         (finish-start)/real(count_rate,dp), ' seconds (array)'
!
      call show_err ( maxval(abs(rval-rref)), eps ) 
!
      write(*,'(/,"Time consumed: Direct / This (serial) = ", F7.4)') dt(1) / dt(7)
!
   end block blk_7
!
10 format ( 'i =', i9, ', string(i) =', a, ', zval(i) =', e25.17 )
11 format ( /, 80('-'), /, a )
!
   deallocate ( strs, rval, rref ) 
!
   STOP 
!
   contains 
!======================================================================
!
! This is the ran0 in Numerical Rrecipes in Fortran.
! To initialize, 
! call system_clock( idummy )
! Do not alter idummy between successive deviates.
!
      function util_unirand ( idum ) result( ran0 )
!
      implicit none 
      integer,parameter :: rk = kind(1.0D0), ik = 4
!
! ARGUMENTS:
!
      real(rk) :: ran0
      integer(ik),intent(in),optional :: idum 
!
! local constants and variable
!
      integer(ik) :: k
      integer(ik),save :: idummy 
      integer(ik),parameter :: &
         ia=16807, im=2147483647, iq=127773, ir=2836, mask=123459876
      real(rk),parameter :: am = 1.0_rk / im
!
!
      if ( present(idum) ) call system_clock( idummy )
!
      idummy = ieor( idummy, mask )
      k = idummy / iq
      idummy = ia*( idummy - k*iq ) - ir*k
      if ( idummy .lt. 0 ) idummy = idummy + im
      ran0 = am*idummy
      idummy = ieor( idummy, mask )
      return
      end function
!
!=====
      subroutine show_err ( er, ep ) 
      real(dp),intent(in) :: er, ep 
      real(dp) :: tmp 
!
      tmp = ep - er
      write(*,10) er, ep, tmp 
      if ( tmp .lt. 0.0_dp ) write(*,11) er/ep 
      return 
 10 format( 'A = Max |rval(:)-rref(:)| =', 1pe10.3,',', 3x, &
              'B = epsilon(1.0d0) =', 1pe10.3,',', 3x, &
              'B-A =', 1pe11.3 )   
 11 format( '*** WARNING: A > B, A/B =', 1pe11.3 )
      end subroutine 
!======================================================================
   end program 

```

1. Result:

> N = 1000000 , i0 = 847605  
> Write: time consumed = 0.7626 seconds (serial)
> 
> * * *
> 
> BLOCK 1: Direct reading formatted string to double  
> Read: time consumed = 0.6423 seconds (serial)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1039 seconds (serial)  
> Read: time consumed = 0.1060 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> Time consumed: Direct / This (serial) = 6.1831
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0922 seconds (serial)  
> Read: time consumed = 0.0896 seconds (array)  
> A = Max |rval(:)-rref(:)| = 1.887E-15, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.665E-15  
> \*\*\* WARNING: A \> B, A/B = 8.500E+00
> 
> Time consumed: Direct / This (serial) = 6.9675
> 
> * * *
> 
> BLOCK 4: F str2real (Carltoffel)  
> Read: time consumed = 0.0525 seconds (serial)  
> Read: time consumed = 0.0501 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 12.2401
> 
> * * *
> 
> BLOCK 5: F str2real\_02 (hkvzjal - version 01)  
> Read: time consumed = 0.0407 seconds (serial)  
> Read: time consumed = 0.0408 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 15.7848
> 
> * * *
> 
> BLOCK 6: F str2real\_03 (hkvzjal - version 02)  
> Read: time consumed = 0.0405 seconds (serial)  
> Read: time consumed = 0.0410 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 15.8451
> 
> * * *
> 
> BLOCK 7: F str2real\_04 (hkvzjal - version 03)  
> Read: time consumed = 0.0370 seconds (serial)  
> Read: time consumed = 0.0381 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 17.3503

Here, “Time consumed: Direct / This (serial) = 17.3503” means 0.6423/0.0370 = 17.35xxx. It shows the speed of the method with UPDATE 2 is fastest!

---

<div class="post-metadata">

**Author:** ![tqviet](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/tqviet/32/5655_2.png) [@tqviet](https://fortran-lang.discourse.group/u/tqviet)\
**Post date:** [January 10, 2023, 9:21am UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/62 "2023-01-10T09:21:52Z")

</div>

Update: Since I replaced the findloc intrinsic function (which is not available in my old compiler), there may be a bug in the following line:

```auto
   if ( period_loc == 0 ) period_loc = exponent_loc

```

Hence, I use a logical variable (i.e. not\_found) to check if the findloc does not find “period”. Herein I also include all versions in only one module, i.e. remove the module mod\_str2real\_02, therefore, we should remove the line

```auto
   use mod_str2real_02

```

from the main program above. The code runs a bit faster. (But I’m not sure if this change make such good effect.)

Here is the new version:

```auto
!------------------------------------------------------------------------------  
   module mod_str2real
!
   implicit none
   private
!
   public :: str2real
   public :: str2real_02
   public :: str2real_03
   public :: str2real_04
!  
   integer, parameter :: wp = kind(1.0d0)
   integer, parameter :: ikind = selected_int_kind(2)
!
   integer(kind=ikind), parameter :: digit_0 = ichar('0')
   integer(kind=ikind), parameter :: period = ichar('.') - digit_0
   integer(kind=ikind), parameter :: minus_sign = ichar('-') - digit_0
!   
   real(wp), parameter :: whole_number_base(*) = &
           [1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, &
            1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0]
   real(wp), parameter :: period_skip = 0d0
   real(wp), parameter :: fractional_base(*) = &
           [1d-1, 1d-2, 1d-3, 1d-4, 1d-5, 1d-6, 1d-7, 1d-8, &
            1d-9, 1d-10, 1d-11, 1d-12, 1d-13, 1d-14, 1d-15, 1d-16, &
            1d-17, 1d-18, 1d-19, 1d-20, 1d-21, 1d-22, 1d-23, 1d-24]
   real(wp), parameter :: base(*) = &
      [whole_number_base, period_skip, fractional_base]
!
   real(wp), parameter :: expbase(*) = &
      [whole_number_base, fractional_base]
!------------------------------------------------------------------------------   
   contains
!------------------------------------------------------------------------------   
   elemental &
   function str2real (s) result(r)
   character(*), intent(in) :: s
   real(wp) :: r
!   
   real(wp) :: r_coefficient
   real(wp) :: r_exponent
!
! integer, parameter :: r10 = selected_real_kind ( P=16 ) !<-- not more accurate
! real(r10) :: r_coefficient
! real(r10) :: r_exponent
!   
   integer, parameter :: N = 32
   character(N) :: factors_char
   integer(kind=ikind) :: factors(N)
   integer(kind=ikind) :: mask(N)
   integer :: period_loc
   integer :: exponent_loc
   integer :: mask_from
   integer :: mask_till
! intrinsic :: findloc
! integer :: findloc
   intrinsic :: scan
   integer :: scan
   logical :: not_found 
!   
   equivalence(factors, factors_char)
!
   factors_char = s
   factors = factors - digit_0
!   
!! period_loc = findloc(factors, period, 1)
!! if ( period_loc == 0 ) period_loc = exponent_loc
!
   not_found = .true. 
   do period_loc = 1,N
      if ( factors(period_loc) == period ) then 
         not_found = .false.
         exit 
      endif 
   enddo
   exponent_loc = scan(s, 'eE', back=.true.)
   if ( exponent_loc == 0 ) exponent_loc = len(s) + 1
   if ( not_found ) period_loc = exponent_loc  
!   
   where (0 <= factors .and. factors <= 9)
      mask = 1
   elsewhere
      mask = 0
   end where
!   
   mask_from = 18 - period_loc
   mask_till = mask_from + exponent_loc - 2
!   
   r_coefficient = sum( &
           factors(:exponent_loc - 1) * &
           base(mask_from:mask_till) * &
           mask(:exponent_loc - 1) )
   r_exponent = sum( &
           factors(exponent_loc+1:len(s)) * &
           mask(exponent_loc+1:len(s)) * &
           base(17-(len(s)-exponent_loc):16) )
   if ( factors(exponent_loc+1) == minus_sign ) r_exponent = -r_exponent
   if ( factors(1) == minus_sign ) r_coefficient = -r_coefficient
   r = r_coefficient * (10**r_exponent)
   return 
   end function str2real
!------------------------------------------------------------------------------  
! hkvzjal - version 01 
!
      elemental &
      function str2real_02(s) result(r)
      character(*), intent(in) :: s
      real(wp) :: r
      real(wp) :: r_coefficient
      integer, parameter :: N = 32
      character(N) :: factors_char
      integer(kind=ikind) :: factors(N)
      integer(kind=ikind) :: mask(N)
      integer :: period_loc
      integer :: exponent_loc
      integer :: exponent_end
      integer :: mask_from
      integer :: mask_till
!! intrinsic :: findloc
!! integer :: findloc
      intrinsic :: scan
      integer :: scan
      integer(1) :: sig_coef, sig_exp
      integer :: i_exponent
      logical :: not_found 
!
      equivalence(factors, factors_char)
!
      sig_coef = 1; sig_exp = 1
      factors_char = s
      factors = factors - digit_0
!
!! period_loc = findloc(factors, period, 1)
!! if ( period_loc == 0 ) period_loc = exponent_loc
!
      not_found = .true. 
      do period_loc = 1,N
         if ( factors(period_loc) == period ) then 
            not_found = .false.
            exit 
         endif 
      enddo
      exponent_loc = scan( s, 'eE', back=.true. )
      if ( exponent_loc == 0 ) exponent_loc = len(s) + 1
      if ( not_found ) period_loc = exponent_loc  
!
      where (0 <= factors .and. factors <= 9)
         mask = 1
      elsewhere
         mask = 0
      end where
!      
      if ( factors(exponent_loc+1) == minus_sign ) sig_exp = -1
      if ( factors(1) == minus_sign ) sig_coef = -1
!      
      mask_from = 18 - period_loc
      mask_till = mask_from + exponent_loc - 2
!      
      r_coefficient = sum( &
              factors(:exponent_loc - 1) * &
              base(mask_from:mask_till) * &
              mask(:exponent_loc - 1))
!  
      i_exponent = str2int( s(exponent_loc+1:len(s)) )
      r = (sig_coef*r_coefficient) * expbase(16-sig_exp*i_exponent)
      return 
      end function
!------------------------------------------------------------------------------  
      elemental &
      function str2int(s) result(iou)
      character(*), intent(in) :: s
      integer :: iou
      integer :: i, val
      iou = 0
      do i = 1, len(s)
         val = iachar(s(i:i))-digit_0
         if( val >= 0 .and. val <= 9 ) iou = iou*10 + val
      end do
      return 
      end function
!------------------------------------------------------------------------------  
! hkvzjal - version 02 
!
      elemental &
      function str2real_03(s) result(r)
      character(*), intent(in) :: s
      real(wp) :: r
      real(wp) :: r_fraction
      integer, parameter :: N = 32
      character(N) :: factors_char
      integer(kind=ikind) :: factors(N)
      integer :: period_loc
      integer :: exponent_loc
!! intrinsic :: findloc
!! integer :: findloc
      intrinsic :: scan
      integer :: scan
      integer(1) :: sig_coef, sig_exp
      integer :: i_exponent, i, val
      logical :: not_found 
!      
      equivalence(factors, factors_char)
!      
      sig_coef = 1; sig_exp = 1
      factors_char = s
      factors = factors - digit_0
!
!! period_loc = findloc(factors, period, 1)
!! if (period_loc == 0) period_loc = exponent_loc
!
      not_found = .true. 
      do period_loc = 1,N
         if ( factors(period_loc) == period ) then 
            not_found = .false.
            exit 
         endif 
      enddo
      exponent_loc = scan( s, 'eE', back=.true. )
      if ( exponent_loc == 0 ) exponent_loc = len(s) + 1
      if ( not_found ) period_loc = exponent_loc  
!
      if ( factors(exponent_loc+1) == minus_sign ) sig_exp = -1
      if ( factors(1) == minus_sign ) sig_coef = -1
      val = str2int( s(1:period_loc-1) )
      r = val
      do i = period_loc+1, exponent_loc-1
         val = factors(i)
         if( val >= 0 .and. val <= 9 ) &
            r = r + val*fractional_base(i-period_loc) 
      end do
      r = sig_coef*r
      i_exponent = str2int( s(exponent_loc+1:len(s)) )
      if(i_exponent.ne.0) r = r * expbase(16-sig_exp*i_exponent)
      return 
      end function
!------------------------------------------------------------------------------  
! hkvzjal - version 03 (the fastest one)
!
      elemental &
      function str2real_04 (s) result(r)
      character(*), intent(in) :: s
      real(wp) :: r
      real(wp) :: r_fraction
      integer, parameter :: N = 32
      character(N) :: factors_char
      integer(kind=ikind) :: factors(N)
      integer :: period_loc
      integer :: exponent_loc
!! intrinsic :: findloc
!! integer :: findloc
      intrinsic :: scan
      integer :: scan
      integer(1) :: sig_coef, sig_exp
      integer :: i_exponent, i, val
      logical :: not_found 
!      
      equivalence(factors, factors_char)
!      
      sig_coef = 1; sig_exp = 1
      factors_char = s
      factors = factors - digit_0
!
!! period_loc = findloc(factors, period, 1)
!! if (period_loc == 0) period_loc = exponent_loc
!
      not_found = .true. 
      do period_loc = 1,N
         if ( factors(period_loc) == period ) then 
            not_found = .false.
            exit 
         endif 
      enddo
      exponent_loc = scan( s, 'eE', back=.true. )
      if ( exponent_loc == 0 ) exponent_loc = len(s) + 1
      if ( not_found ) period_loc = exponent_loc  
!
      if ( factors(exponent_loc+1) == minus_sign ) sig_exp = -1
      if ( factors(1) == minus_sign ) sig_coef = -1
      val = str2int( s(1:period_loc-1) )
      r = sum( factors(period_loc+1:exponent_loc-1) * &
               fractional_base(1:exponent_loc-period_loc-1) )
      r = sig_coef*(r+val)
      i_exponent = str2int( s(exponent_loc+1:len(s)) )
      if(i_exponent.ne.0) r = r * expbase(16-sig_exp*i_exponent)
      return 
      end function
!------------------------------------------------------------------------------  
!------------------------------------------------------------------------------   
   end module mod_str2real
!------------------------------------------------------------------------------

```

and new result:

> N = 1000000 , i0 = 55962  
> Write: time consumed = 0.7679 seconds (serial)
> 
> * * *
> 
> BLOCK 1: Direct reading formatted string to double  
> Read: time consumed = 0.6444 seconds (serial)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> * * *
> 
> BLOCK 2: C st\_to\_r8  
> Read: time consumed = 0.1050 seconds (serial)  
> Read: time consumed = 0.1047 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-17, B = epsilon(1.0d0) = 2.220E-16, B-A = 1.665E-16
> 
> Time consumed: Direct / This (serial) = 6.1357
> 
> * * *
> 
> BLOCK 3: F st\_to\_r8  
> Read: time consumed = 0.0910 seconds (serial)  
> Read: time consumed = 0.0915 seconds (array)  
> A = Max |rval(:)-rref(:)| = 1.887E-15, B = epsilon(1.0d0) = 2.220E-16, B-A = -1.665E-15  
> \*\*\* WARNING: A \> B, A/B = 8.500E+00
> 
> Time consumed: Direct / This (serial) = 7.0826
> 
> * * *
> 
> BLOCK 4: F str2real (Carltoffel)  
> Read: time consumed = 0.0576 seconds (serial)  
> Read: time consumed = 0.0589 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 11.1965
> 
> * * *
> 
> BLOCK 5: F str2real\_02 (hkvzjal - version 01)  
> Read: time consumed = 0.0400 seconds (serial)  
> Read: time consumed = 0.0407 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 16.1034
> 
> * * *
> 
> BLOCK 6: F str2real\_03 (hkvzjal - version 02)  
> Read: time consumed = 0.0375 seconds (serial)  
> Read: time consumed = 0.0381 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 17.1991
> 
> * * *
> 
> BLOCK 7: F str2real\_04 (hkvzjal - version 03)  
> Read: time consumed = 0.0346 seconds (serial)  
> Read: time consumed = 0.0361 seconds (array)  
> A = Max |rval(:)-rref(:)| = 5.551E-16, B = epsilon(1.0d0) = 2.220E-16, B-A = -3.331E-16  
> \*\*\* WARNING: A \> B, A/B = 2.500E+00
> 
> Time consumed: Direct / This (serial) = 18.6250

---

<div class="post-metadata">

**Author:** ![everythingfunctional](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/everythingfunctional/32/176_2.png) [@everythingfunctional](https://fortran-lang.discourse.group/u/everythingfunctional)\
**Post date:** [January 10, 2023, 2:37pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/63 "2023-01-10T14:37:45Z")

</div>

@hkvzjal and @Carltoffel ,

A few weeks ago I made an attempt at porting the C++ [fast\_float](https://github.com/fastfloat/fast_float) library to Fortran (which you can find [here](https://gitlab.com/everythingfunctional/ffff)). My goal is to have the interface support reporting an invalid number. However, without the unsigned integers and defined bit representations, the exact algorithm from the C++ library would not be portable Fortran. I initially still tried to follow their algorithm in spirit and found it didn’t end up always being faster than list-directed read. I then tried something (I think) similar to what you’ve done in [str2real](https://github.com/Carltoffel/Fortran-String-to-Real). I still found it wasn’t always faster. Would either of you be willing to try using your algorithm with my interface and test suite?

---

<div class="post-metadata">

**Author:** ![hkvzjal](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/hkvzjal/32/3055_2.png) [@hkvzjal](https://fortran-lang.discourse.group/u/hkvzjal)\
**Post date:** [January 10, 2023, 4:01pm UTC](https://fortran-lang.discourse.group/t/faster-string-to-double/2208/64 "2023-01-10T16:01:43Z")

</div>

Hey @tqviet , thanks a lot for putting everything together!!  
Just for information, I ran the tests 6 times with the findloc and with your boolean version  
Compiler: Intel 19.1.3.311 (x64 architecture)  
CPU: Intel(R) Xeon(R) Gold 6226R CPU @ 2.90GHz 2.89 GHz

(results for the last version)  
**With findloc** :

- Loop: 0.0466s ± 0.0017 standard deviation
- Array: 0.0460s ± 0.001 standard deviation

**With boolean** :

- Loop: 0.0475s ± 0.001 standard deviation
- Array: 0.0462s ± 0.001 standard deviation

All runs with the intrinsic gave me B-A =0,  
Just 1 of the runs of the boolean implementation gave me B-A=-1.1106E-16 (on rref(i)=0.678125232773891 → rval(i) =0.678125232773892 )

@everythingfunctional I’ll take a look and try to come back soon

[Previous page](https://fortran-lang.discourse.group/t/faster-string-to-double/2208.md?page=2)

[Next page](https://fortran-lang.discourse.group/t/faster-string-to-double/2208.md?page=4)
