From c0f38d45d1221e489b3696dd28821d6084fc7d83 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Wed, 1 Nov 2023 00:46:02 +0100 Subject: [PATCH 01/74] Proposal for a reference string to number --- example/strings/CMakeLists.txt | 1 + example/strings/example_str2num.f90 | 22 ++ src/stdlib_str2num.f90 | 380 ++++++++++++++++++++++++++ test/string/test_string_to_number.f90 | 236 ++++++++++++++++ 4 files changed, 639 insertions(+) create mode 100644 example/strings/example_str2num.f90 create mode 100644 src/stdlib_str2num.f90 create mode 100644 test/string/test_string_to_number.f90 diff --git a/example/strings/CMakeLists.txt b/example/strings/CMakeLists.txt index 086140bcb..5ed980646 100644 --- a/example/strings/CMakeLists.txt +++ b/example/strings/CMakeLists.txt @@ -10,3 +10,4 @@ ADD_EXAMPLE(starts_with) ADD_EXAMPLE(strip) ADD_EXAMPLE(to_string) ADD_EXAMPLE(zfill) +ADD_EXAMPLE(str2num) diff --git a/example/strings/example_str2num.f90 b/example/strings/example_str2num.f90 new file mode 100644 index 000000000..9e8d1a7b9 --- /dev/null +++ b/example/strings/example_str2num.f90 @@ -0,0 +1,22 @@ +program example_str2num + use stdlib_kinds, only: dp + use stdlib_str2num + character(:), allocatable, target :: chain + character(len=:), pointer :: cptr + real(dp), allocatable :: r(:), p(:) + integer :: i + + chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" + allocate( r(6), p(6) ) + !> Example for streamline conversion using `str2float_p` + cptr => chain + do i =1, 6 + r(i) = str2float_p( cptr ) !> the pointer is shifted within the function + end do + read(chain,*) p + print *, "Reading with str2num" + print *, r + print *, "Reading with formatted read" + print *, p + +end program \ No newline at end of file diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 new file mode 100644 index 000000000..27df9ee37 --- /dev/null +++ b/src/stdlib_str2num.f90 @@ -0,0 +1,380 @@ +!> The `stdlib_str2num` module provides procedures and interfaces for conversion +!> of characters to numerical types. Currently supported: int32, real32 and real64 +!> +!> This code was modified from https://github.com/jalvesz/Fortran-String-to-Num by Alves Jose +!> And was possible thanks to all the discussions in this thread https://fortran-lang.discourse.group/t/faster-string-to-double/ +!> + +module stdlib_str2num + use iso_fortran_env, only: int32, int64, sp => real32, dp => real64 + implicit none + private + !> easy to use function interfaces + public :: str2int, str2int_p + public :: str2float, str2float_p + public :: str2double, str2double_p + !> generic subroutine interface + public :: str2num + + integer, parameter :: ikind = selected_int_kind(2) + integer(kind=ikind), parameter :: digit_0 = ichar('0',kind=ikind) + integer(kind=ikind), parameter :: period = ichar('.',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: comma = ichar(',',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: minus_sign = ichar('-',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: plus_sign = ichar('+',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: Inf = ichar('I',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: NaN = ichar('N',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: le = ichar('e',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: BE = ichar('E',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: ld = ichar('d',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: BD = ichar('D',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: LF = 10, CR = 13, WS = 32 + + interface str2num + !> version: experimental + module procedure str2int_32 + module procedure str2real_sp + module procedure str2real_dp + end interface + + contains + + !--------------------------------------------- + ! String To Integer interfaces + !--------------------------------------------- + + elemental function str2int(s) result(int) + ! -- In/out Variables + character(*), intent(in) :: s !> input string + integer :: int !> Output integer 32 value + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: stat !> error status + !---------------------------------------------- + call str2num(s,int,p,stat) + end function + + function str2int_p(s,stat) result(int) + ! -- In/out Variables + character(len=:), pointer :: s !> input string + integer :: int !> Output integer 32 value + integer(1),intent(inout), optional :: stat + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: err + !---------------------------------------------- + call str2num(s,int,p,err) + p = min( p , len(s) ) + s => s(p:) + if(present(stat)) stat = err + end function + + elemental subroutine str2int_32(s,v,p,stat) + !> Return an unsigned 32-bit integer + ! -- In/out Variables + character(*), intent(in) :: s !> input string + integer, intent(inout) :: v !> Output real value + integer(1), intent(out) :: p !> position within the number + integer(1), intent(out) :: stat !> status upon succes or failure to read + ! -- Internal Variables + integer(1) :: val + !---------------------------------------------- + stat = 23 !> initialize error status with any number > 0 + !---------------------------------------------- + ! Find first non white space + p = mvs2nwsp(s) + !---------------------------------------------- + v = 0 + do while( p<=len(s) ) + val = iachar(s(p:p))-digit_0 + if( val >= 0 .and. val <= 9) then + v = v*10 + val ; p = p + 1 + else + exit + end if + end do + stat = 0 + end subroutine + + !--------------------------------------------- + ! String To Real function interfaces + !--------------------------------------------- + + elemental function str2float(s) result(r) + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(sp) :: r !> Output real value + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: stat ! error status + !---------------------------------------------- + call str2num(s,r,p,stat) + end function + + function str2float_p(s,stat) result(r) + ! -- In/out Variables + character(len=:), pointer :: s !> input string + real(sp) :: r !> Output real value + integer(1),intent(inout), optional :: stat + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: err + !---------------------------------------------- + call str2num(s,r,p,err) + p = min( p , len(s) ) + s => s(p:) + if(present(stat)) stat = err + end function + + elemental subroutine str2real_sp(s,v,p,stat) + integer, parameter :: wp = sp + !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(wp), intent(inout) :: v !> Output real value + integer(1), intent(out) :: p !> last position within the string + integer(1), intent(out) :: stat !> status upon success or failure to read + + ! -- Internal Variables + real(sp), parameter :: rnan = transfer(int(B'01111111101000000000000000000000',int32), 1._sp) + integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors + integer(kind=ikind), parameter :: nfnb = 40 !> number of fractional number factors + real(wp), parameter :: whole_number_base(nwnb) = & + [ 1e38, 1e37, 1e36, 1e35, 1e34, 1e33, 1e32, & + 1e31, 1e30, 1e29, 1e28, 1e27, 1e26, 1e25, 1e24, & + 1e23, 1e22, 1e21, 1e20, 1e19, 1e18, 1e17, 1e16, & + 1e15, 1e14, 1e13, 1e12, 1e11, 1e10, 1e9, 1e8, & + 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0] + real(wp), parameter :: fractional_base(nfnb) = & + [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, 1e-19, 1e-20, 1e-21, 1e-22, 1e-23, 1e-24, & + 1e-25, 1e-26, 1e-27, 1e-28, 1e-29, 1e-30, 1e-31, 1e-32, & + 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39, 1e-40 ] + real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] + + integer(1) :: sign, sige !> sign of integer number and exponential + integer(wp) :: int_wp !> long integer to capture fractional part + integer :: i_exp !> integer to capture whole number part + integer(1) :: i, pP, pE, val , resp + !---------------------------------------------- + stat = 23 !> initialize error status with any number > 0 + !---------------------------------------------- + ! Find first non white space + p = mvs2nwsp(s) + !---------------------------------------------- + ! Verify leading negative + sign = 1 + if( iachar(s(p:p)) == minus_sign+digit_0 ) then + sign = -1 ; p = p + 1 + end if + if( iachar(s(p:p)) == Inf ) then + v = sign*huge(1_wp); return + else if( iachar(s(p:p)) == NaN ) then + v = rNaN; return + end if + !---------------------------------------------- + ! read whole and fractional number in a single integer + pP = 127 + int_wp = 0 + do i = p, min(10+p-1,len(s)) + val = iachar(s(i:i))-digit_0 + if( val >= 0 .and. val <= 9 ) then + int_wp = int_wp*10 + val + else if( val == period ) then + pP = i + else + exit + end if + end do + pE = i ! Fix the exponent position + do while( i<=len(s) ) + val = iachar(s(i:i))-digit_0 + if( val < 0 .or. val > 9 ) exit + i = i + 1 + end do + p = i + resp = pE-min(pP,p) ! If no decimal indicator found it is taken as being in the current p position + if( resp <= 0 ) resp = resp+1 + !---------------------------------------------- + ! Get exponential + sige = 1 + if( p= 0 .and. val <= 9) then + i_exp = i_exp*10_ikind + val ; p = p + 1 + else + exit + end if + end do + + v = sign*int_wp*expbase(nwnb-1+resp-sige*max(0,i_exp)) + stat = 0 + end subroutine + + !--------------------------------------------- + ! String To Double function interfaces + !--------------------------------------------- + + elemental function str2double(s) result(r) + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(dp) :: r !> Output real value + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: stat ! error status + !---------------------------------------------- + call str2num(s,r,p,stat) + end function + + function str2double_p(s,stat) result(r) + ! -- In/out Variables + character(len=:), pointer :: s !> input string + real(dp) :: r !> Output real value + integer(1),intent(inout), optional :: stat + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: err + !---------------------------------------------- + call str2num(s,r,p,err) + p = min( p , len(s) ) + s => s(p:) + if(present(stat)) stat = err + end function + + elemental subroutine str2real_dp(s,v,p,stat) + integer, parameter :: wp = dp + !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(wp), intent(inout) :: v !> Output real value + integer(1), intent(out) :: p !> last position within the string + integer(1), intent(out) :: stat !> status upon success or failure to read + + ! -- Internal Variables + real(dp), parameter :: rNaN = TRANSFER(9218868437227405313_int64, 1._dp) + integer(kind=ikind), parameter :: nwnb = 40 !> number of whole number factors + integer(kind=ikind), parameter :: nfnb = 40 !> number of fractional number factors + real(wp), parameter :: whole_number_base(nwnb) = & + [1d39, 1d38, 1d37, 1d36, 1d35, 1d34, 1d33, 1d32, & + 1d31, 1d30, 1d29, 1d28, 1d27, 1d26, 1d25, 1d24, & + 1d23, 1d22, 1d21, 1d20, 1d19, 1d18, 1d17, 1d16, & + 1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, & + 1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0] + real(wp), parameter :: fractional_base(nfnb) = & + [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, & + 1d-25, 1d-26, 1d-27, 1d-28, 1d-29, 1d-30, 1d-31, 1d-32, & + 1d-33, 1d-34, 1d-35, 1d-36, 1d-37, 1d-38, 1d-39, 1d-40 ] + real(wp), parameter :: period_skip = 0d0 + real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] + + integer(1) :: sign, sige !> sign of integer number and exponential + integer(wp) :: int_wp !> long integer to capture fractional part + integer :: i_exp !> integer to capture whole number part + integer(1) :: i, pP, pE, val , resp + !---------------------------------------------- + stat = 23 !> initialize error status with any number > 0 + !---------------------------------------------- + ! Find first non white space + p = mvs2nwsp(s) + !---------------------------------------------- + ! Verify leading negative + sign = 1 + if( iachar(s(p:p)) == minus_sign+digit_0 ) then + sign = -1 ; p = p + 1 + end if + if( iachar(s(p:p)) == Inf ) then + v = sign*huge(1_wp); return + else if( iachar(s(p:p)) == NaN ) then + v = rNaN; return + end if + !---------------------------------------------- + ! read whole and fractional number in a single integer + pP = 127 + int_wp = 0 + do i = p, min(19+p-1,len(s)) + val = iachar(s(i:i))-digit_0 + if( val >= 0 .and. val <= 9 ) then + int_wp = int_wp*10 + val + else if( val == period ) then + pP = i + else + exit + end if + end do + pE = i ! Fix the exponent position + do while( i<=len(s) ) + val = iachar(s(i:i))-digit_0 + if( val < 0 .or. val > 9 ) exit + i = i + 1 + end do + p = i + resp = pE-min(pP,p) ! If no decimal indicator found it is taken as being in the current p position + if( resp <= 0 ) resp = resp+1 + !---------------------------------------------- + ! Get exponential + sige = 1 + if( p= 0 .and. val <= 9) then + i_exp = i_exp*10_ikind + val ; p = p + 1 + else + exit + end if + end do + + v = sign*int_wp*expbase(nwnb-1+resp-sige*max(0,i_exp)) + stat = 0 + end subroutine + + !--------------------------------------------- + ! Internal Utility functions + !--------------------------------------------- + + elemental function mvs2nwsp(s) result(p) + !> move string to position of the next non white space character + character(*),intent(in) :: s !> character chain + integer(1) :: p !> position + !---------------------------------------------- + p = 1 + do while( p move string to position of the next white space character + character(*),intent(in) :: s !> character chain + integer(1) :: p !> position + !---------------------------------------------- + p = 1 + do while( p Collect all exported unit tests + subroutine collect_string_to_number(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("str2float", test_str2float), & + new_unittest("str2double", test_str2double) & + ] + end subroutine collect_string_to_number + + subroutine test_str2float(error) + use stdlib_str2num, only: str2real => str2float + type(error_type), allocatable, intent(out) :: error + integer, parameter :: wp = sp + + call check(error, ucheck("1.234")) + if (allocated(error)) return + + call check(error, ucheck("1.E1")) + if (allocated(error)) return + + call check(error, ucheck("1e0")) + if (allocated(error)) return + + call check(error, ucheck("0.1234E0")) + if (allocated(error)) return + + call check(error, ucheck("12.34E0")) + if (allocated(error)) return + + call check(error, ucheck("0.34E2")) + if (allocated(error)) return + + call check(error, ucheck(".34e0")) + if (allocated(error)) return + + call check(error, ucheck("34.E1")) + if (allocated(error)) return + + call check(error, ucheck("-34.5E1")) + if (allocated(error)) return + + call check(error, ucheck("0.0021E10")) + if (allocated(error)) return + + call check(error, ucheck("12.21e-1")) + if (allocated(error)) return + + call check(error, ucheck("12.21e+001 ")) + if (allocated(error)) return + + call check(error, ucheck("-1")) + if (allocated(error)) return + + call check(error, ucheck(" -0.23317260678539647E-01 ")) + if (allocated(error)) return + + call check(error, ucheck(" 2.5647869e-003 "//char(13)//char(10))) + if (allocated(error)) return + + call check(error, ucheck("1.-3")) + if (allocated(error)) return + + call check(error, ucheck("Inf")) + if (allocated(error)) return + + call check(error, ucheck("-Inf")) + if (allocated(error)) return + + call check(error, ucheck("NaN")) + if (allocated(error)) return + + call check(error, ucheck("0.123456789123456789123456789123456789")) + if (allocated(error)) return + + call check(error, ucheck("1234567890123456789012345678901234567890-9") ) + if (allocated(error)) return + + call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) + if (allocated(error)) return + + contains + logical function ucheck(s) + character(*), intent(in) :: s + real(wp) :: formatted_read_out + real(wp) :: str2real_out + real(wp) :: abs_err + real(wp) :: rel_err + + ucheck = .true. + read(s,*) formatted_read_out + str2real_out = str2real(s) + abs_err = str2real_out - formatted_read_out + rel_err = abs_err / formatted_read_out + + if(abs(rel_err) > 10*epsilon(0.0_wp)) then + write(*,"('formatted read : ' g0)") formatted_read_out + write(*,"('str2real : ' g0)") str2real_out + write(*,"('difference abs : ' g0)") abs_err + write(*,"('difference rel : ' g0 '%')") rel_err * 100 + ucheck = .false. + end if + end function + end subroutine + + subroutine test_str2double(error) + use stdlib_str2num, only: str2real => str2double + type(error_type), allocatable, intent(out) :: error + integer, parameter :: wp = dp + + call check(error, ucheck("1.234")) + if (allocated(error)) return + + call check(error, ucheck("1.E1")) + if (allocated(error)) return + + call check(error, ucheck("1e0")) + if (allocated(error)) return + + call check(error, ucheck("0.1234E0")) + if (allocated(error)) return + + call check(error, ucheck("12.34E0")) + if (allocated(error)) return + + call check(error, ucheck("0.34E2")) + if (allocated(error)) return + + call check(error, ucheck(".34e0")) + if (allocated(error)) return + + call check(error, ucheck("34.E1")) + if (allocated(error)) return + + call check(error, ucheck("-34.5E1")) + if (allocated(error)) return + + call check(error, ucheck("0.0021E10")) + if (allocated(error)) return + + call check(error, ucheck("12.21e-1")) + if (allocated(error)) return + + call check(error, ucheck("12.21e+001 ")) + if (allocated(error)) return + + call check(error, ucheck("-1")) + if (allocated(error)) return + + call check(error, ucheck(" -0.23317260678539647E-01 ")) + if (allocated(error)) return + + call check(error, ucheck(" 2.5647869e-003 "//char(13)//char(10))) + if (allocated(error)) return + + call check(error, ucheck("1.-3")) + if (allocated(error)) return + + call check(error, ucheck("Inf")) + if (allocated(error)) return + + call check(error, ucheck("-Inf")) + if (allocated(error)) return + + call check(error, ucheck("NaN")) + if (allocated(error)) return + + call check(error, ucheck("0.123456789123456789123456789123456789")) + if (allocated(error)) return + + call check(error, ucheck("1234567890123456789012345678901234567890-9") ) + if (allocated(error)) return + + call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) + if (allocated(error)) return + + contains + logical function ucheck(s) + character(*), intent(in) :: s + real(wp) :: formatted_read_out + real(wp) :: str2real_out + real(wp) :: abs_err + real(wp) :: rel_err + + ucheck = .true. + read(s,*) formatted_read_out + str2real_out = str2real(s) + abs_err = str2real_out - formatted_read_out + rel_err = abs_err / formatted_read_out + + if(abs(rel_err) > 10*epsilon(0.0_wp)) then + write(*,"('formatted read : ' g0)") formatted_read_out + write(*,"('str2real : ' g0)") str2real_out + write(*,"('difference abs : ' g0)") abs_err + write(*,"('difference rel : ' g0 '%')") rel_err * 100 + ucheck = .false. + end if + end function + end subroutine + +end module test_string_to_number + +program tester + use, intrinsic :: iso_fortran_env, only : error_unit + use testdrive, only : run_testsuite, new_testsuite, testsuite_type + use test_string_to_number, only : collect_string_to_number + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("string_to_number", collect_string_to_number) & + ] + + do is = 1, size(testsuites) + write(error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if +end program \ No newline at end of file From 94cc5dfa959191911e03585d23adb9e33fce2cd6 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Wed, 1 Nov 2023 11:07:10 +0100 Subject: [PATCH 02/74] str2num: forgot to include file in CMakeLists.txt --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a6fe66cc..4fdf49b64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC stdlib_specialfunctions_legendre.f90 stdlib_quadrature_gauss.f90 stdlib_stringlist_type.f90 + stdlib_str2num.f90 ${outFiles} ) From 001ef27296792360ce46f81fd450a2d972450a74 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Thu, 2 Nov 2023 19:11:40 +0100 Subject: [PATCH 03/74] update interface str2num > to_num( ) using mold --- example/strings/example_str2num.f90 | 6 +- src/stdlib_str2num.f90 | 229 +++++++++++++------------- test/string/test_string_to_number.f90 | 36 ++-- 3 files changed, 142 insertions(+), 129 deletions(-) diff --git a/example/strings/example_str2num.f90 b/example/strings/example_str2num.f90 index 9e8d1a7b9..0d5203f25 100644 --- a/example/strings/example_str2num.f90 +++ b/example/strings/example_str2num.f90 @@ -8,13 +8,13 @@ program example_str2num chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" allocate( r(6), p(6) ) - !> Example for streamline conversion using `str2float_p` + !> Example for streamline conversion using `to_num_p` cptr => chain do i =1, 6 - r(i) = str2float_p( cptr ) !> the pointer is shifted within the function + r(i) = to_num_p( cptr , r(i) ) !> the pointer is shifted within the function end do read(chain,*) p - print *, "Reading with str2num" + print *, "Reading with to_num" print *, r print *, "Reading with formatted read" print *, p diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 27df9ee37..869c56523 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -9,12 +9,7 @@ module stdlib_str2num use iso_fortran_env, only: int32, int64, sp => real32, dp => real64 implicit none private - !> easy to use function interfaces - public :: str2int, str2int_p - public :: str2float, str2float_p - public :: str2double, str2double_p - !> generic subroutine interface - public :: str2num + public :: to_num, to_num_p integer, parameter :: ikind = selected_int_kind(2) integer(kind=ikind), parameter :: digit_0 = ichar('0',kind=ikind) @@ -30,46 +25,119 @@ module stdlib_str2num integer(kind=ikind), parameter :: BD = ichar('D',kind=ikind) - digit_0 integer(kind=ikind), parameter :: LF = 10, CR = 13, WS = 32 - interface str2num - !> version: experimental - module procedure str2int_32 - module procedure str2real_sp - module procedure str2real_dp + interface to_num + module procedure to_int + module procedure to_float + module procedure to_double + end interface + + interface to_num_p + module procedure to_int_p + module procedure to_float_p + module procedure to_double_p + end interface + + interface to_num_base + module procedure to_int_32 + module procedure to_real_sp + module procedure to_real_dp end interface contains !--------------------------------------------- - ! String To Integer interfaces + ! String To Number interfaces !--------------------------------------------- - elemental function str2int(s) result(int) + function to_int(s,mold) result(v) ! -- In/out Variables character(*), intent(in) :: s !> input string - integer :: int !> Output integer 32 value + integer, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + integer :: v !> Output integer 32 value ! -- Internal Variables integer(1) :: p !> position within the number integer(1) :: stat !> error status !---------------------------------------------- - call str2num(s,int,p,stat) + call to_num_base(s,v,p,stat) + end function + + function to_int_p(s,mold,stat) result(v) + ! -- In/out Variables + character(len=:), pointer :: s !> input string + integer, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + integer :: v !> Output integer 32 value + integer(1),intent(inout), optional :: stat + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: err + !---------------------------------------------- + call to_num_base(s,v,p,err) + p = min( p , len(s) ) + s => s(p:) + if(present(stat)) stat = err + end function + + function to_float(s,mold) result(r) + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + real(sp) :: r !> Output real value + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: stat ! error status + !---------------------------------------------- + call to_num_base(s,r,p,stat) end function - function str2int_p(s,stat) result(int) + function to_float_p(s,mold,stat) result(r) ! -- In/out Variables character(len=:), pointer :: s !> input string - integer :: int !> Output integer 32 value + real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + real(sp) :: r !> Output real value integer(1),intent(inout), optional :: stat ! -- Internal Variables integer(1) :: p !> position within the number integer(1) :: err !---------------------------------------------- - call str2num(s,int,p,err) + call to_num_base(s,r,p,err) p = min( p , len(s) ) s => s(p:) if(present(stat)) stat = err end function - elemental subroutine str2int_32(s,v,p,stat) + function to_double(s,mold) result(r) + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + real(dp) :: r !> Output real value + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: stat ! error status + !---------------------------------------------- + call to_num_base(s,r,p,stat) + end function + + function to_double_p(s,mold,stat) result(r) + ! -- In/out Variables + character(len=:), pointer :: s !> input string + real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + real(dp) :: r !> Output real value + integer(1),intent(inout), optional :: stat + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: err + !---------------------------------------------- + call to_num_base(s,r,p,err) + p = min( p , len(s) ) + s => s(p:) + if(present(stat)) stat = err + end function + + !--------------------------------------------- + ! String To Number Implementations + !--------------------------------------------- + + subroutine to_int_32(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables character(*), intent(in) :: s !> input string @@ -95,38 +163,8 @@ elemental subroutine str2int_32(s,v,p,stat) end do stat = 0 end subroutine - - !--------------------------------------------- - ! String To Real function interfaces - !--------------------------------------------- - - elemental function str2float(s) result(r) - ! -- In/out Variables - character(*), intent(in) :: s !> input string - real(sp) :: r !> Output real value - ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: stat ! error status - !---------------------------------------------- - call str2num(s,r,p,stat) - end function - - function str2float_p(s,stat) result(r) - ! -- In/out Variables - character(len=:), pointer :: s !> input string - real(sp) :: r !> Output real value - integer(1),intent(inout), optional :: stat - ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: err - !---------------------------------------------- - call str2num(s,r,p,err) - p = min( p , len(s) ) - s => s(p:) - if(present(stat)) stat = err - end function - elemental subroutine str2real_sp(s,v,p,stat) + subroutine to_real_sp(s,v,p,stat) integer, parameter :: wp = sp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables @@ -138,24 +176,16 @@ elemental subroutine str2real_sp(s,v,p,stat) ! -- Internal Variables real(sp), parameter :: rnan = transfer(int(B'01111111101000000000000000000000',int32), 1._sp) integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors - integer(kind=ikind), parameter :: nfnb = 40 !> number of fractional number factors - real(wp), parameter :: whole_number_base(nwnb) = & - [ 1e38, 1e37, 1e36, 1e35, 1e34, 1e33, 1e32, & - 1e31, 1e30, 1e29, 1e28, 1e27, 1e26, 1e25, 1e24, & - 1e23, 1e22, 1e21, 1e20, 1e19, 1e18, 1e17, 1e16, & - 1e15, 1e14, 1e13, 1e12, 1e11, 1e10, 1e9, 1e8, & - 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0] - real(wp), parameter :: fractional_base(nfnb) = & - [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, 1e-19, 1e-20, 1e-21, 1e-22, 1e-23, 1e-24, & - 1e-25, 1e-26, 1e-27, 1e-28, 1e-29, 1e-30, 1e-31, 1e-32, & - 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39, 1e-40 ] + integer(kind=ikind), parameter :: nfnb = 37 !> number of fractional number factors + integer :: e + real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] + real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] integer(1) :: sign, sige !> sign of integer number and exponential integer(wp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part + integer :: exp_aux integer(1) :: i, pP, pE, val , resp !---------------------------------------------- stat = 23 !> initialize error status with any number > 0 @@ -218,42 +248,19 @@ elemental subroutine str2real_sp(s,v,p,stat) exit end if end do - - v = sign*int_wp*expbase(nwnb-1+resp-sige*max(0,i_exp)) + + exp_aux = nwnb-1+resp-sige*max(0,i_exp) + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then + v = sign*int_wp*expbase(exp_aux) + else if(exp_aux>nwnb+nfnb) then + v = sign*int_wp*fractional_base(exp_aux-(nwnb+nfnb))*expbase(nwnb+nfnb) + else + v = sign*int_wp*10._wp**(sige*max(0,i_exp)-resp) + end if stat = 0 end subroutine - !--------------------------------------------- - ! String To Double function interfaces - !--------------------------------------------- - - elemental function str2double(s) result(r) - ! -- In/out Variables - character(*), intent(in) :: s !> input string - real(dp) :: r !> Output real value - ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: stat ! error status - !---------------------------------------------- - call str2num(s,r,p,stat) - end function - - function str2double_p(s,stat) result(r) - ! -- In/out Variables - character(len=:), pointer :: s !> input string - real(dp) :: r !> Output real value - integer(1),intent(inout), optional :: stat - ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: err - !---------------------------------------------- - call str2num(s,r,p,err) - p = min( p , len(s) ) - s => s(p:) - if(present(stat)) stat = err - end function - - elemental subroutine str2real_dp(s,v,p,stat) + subroutine to_real_dp(s,v,p,stat) integer, parameter :: wp = dp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables @@ -265,25 +272,16 @@ elemental subroutine str2real_dp(s,v,p,stat) ! -- Internal Variables real(dp), parameter :: rNaN = TRANSFER(9218868437227405313_int64, 1._dp) integer(kind=ikind), parameter :: nwnb = 40 !> number of whole number factors - integer(kind=ikind), parameter :: nfnb = 40 !> number of fractional number factors - real(wp), parameter :: whole_number_base(nwnb) = & - [1d39, 1d38, 1d37, 1d36, 1d35, 1d34, 1d33, 1d32, & - 1d31, 1d30, 1d29, 1d28, 1d27, 1d26, 1d25, 1d24, & - 1d23, 1d22, 1d21, 1d20, 1d19, 1d18, 1d17, 1d16, & - 1d15, 1d14, 1d13, 1d12, 1d11, 1d10, 1d9, 1d8, & - 1d7, 1d6, 1d5, 1d4, 1d3, 1d2, 1d1, 1d0] - real(wp), parameter :: fractional_base(nfnb) = & - [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, & - 1d-25, 1d-26, 1d-27, 1d-28, 1d-29, 1d-30, 1d-31, 1d-32, & - 1d-33, 1d-34, 1d-35, 1d-36, 1d-37, 1d-38, 1d-39, 1d-40 ] - real(wp), parameter :: period_skip = 0d0 + integer(kind=ikind), parameter :: nfnb = 64 !> number of fractional number factors + integer :: e + real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] + real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] integer(1) :: sign, sige !> sign of integer number and exponential integer(wp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part + integer :: exp_aux integer(1) :: i, pP, pE, val , resp !---------------------------------------------- stat = 23 !> initialize error status with any number > 0 @@ -346,8 +344,15 @@ elemental subroutine str2real_dp(s,v,p,stat) exit end if end do - - v = sign*int_wp*expbase(nwnb-1+resp-sige*max(0,i_exp)) + + exp_aux = nwnb-1+resp-sige*max(0,i_exp) + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then + v = sign*int_wp*expbase(exp_aux) + else if(exp_aux>nwnb+nfnb) then + v = sign*int_wp*fractional_base(exp_aux-(nwnb+nfnb))*expbase(nwnb+nfnb) + else + v = sign*int_wp*10._wp**(sige*max(0,i_exp)-resp) + end if stat = 0 end subroutine diff --git a/test/string/test_string_to_number.f90 b/test/string/test_string_to_number.f90 index be241fa8c..cf4156c66 100644 --- a/test/string/test_string_to_number.f90 +++ b/test/string/test_string_to_number.f90 @@ -12,13 +12,13 @@ subroutine collect_string_to_number(testsuite) type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & - new_unittest("str2float", test_str2float), & - new_unittest("str2double", test_str2double) & + new_unittest("to_float", test_to_float), & + new_unittest("to_double", test_to_double) & ] end subroutine collect_string_to_number - subroutine test_str2float(error) - use stdlib_str2num, only: str2real => str2float + subroutine test_to_float(error) + use stdlib_str2num type(error_type), allocatable, intent(out) :: error integer, parameter :: wp = sp @@ -88,23 +88,27 @@ subroutine test_str2float(error) call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) if (allocated(error)) return + call check(error, ucheck("0.140129846432481707092372958328991613128026194187651577"//& + & "175706828388979108268586060148663818836212158203125E-44")) + if (allocated(error)) return + contains logical function ucheck(s) character(*), intent(in) :: s real(wp) :: formatted_read_out - real(wp) :: str2real_out + real(wp) :: to_num_out real(wp) :: abs_err real(wp) :: rel_err ucheck = .true. read(s,*) formatted_read_out - str2real_out = str2real(s) - abs_err = str2real_out - formatted_read_out + to_num_out = to_num(s, to_num_out) + abs_err = to_num_out - formatted_read_out rel_err = abs_err / formatted_read_out if(abs(rel_err) > 10*epsilon(0.0_wp)) then write(*,"('formatted read : ' g0)") formatted_read_out - write(*,"('str2real : ' g0)") str2real_out + write(*,"('to_num : ' g0)") to_num_out write(*,"('difference abs : ' g0)") abs_err write(*,"('difference rel : ' g0 '%')") rel_err * 100 ucheck = .false. @@ -112,8 +116,8 @@ logical function ucheck(s) end function end subroutine - subroutine test_str2double(error) - use stdlib_str2num, only: str2real => str2double + subroutine test_to_double(error) + use stdlib_str2num type(error_type), allocatable, intent(out) :: error integer, parameter :: wp = dp @@ -183,23 +187,27 @@ subroutine test_str2double(error) call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) if (allocated(error)) return + call check(error, ucheck("0.140129846432481707092372958328991613128026194187651577"//& + & "175706828388979108268586060148663818836212158203125E-44")) + if (allocated(error)) return + contains logical function ucheck(s) character(*), intent(in) :: s real(wp) :: formatted_read_out - real(wp) :: str2real_out + real(wp) :: to_num_out real(wp) :: abs_err real(wp) :: rel_err ucheck = .true. read(s,*) formatted_read_out - str2real_out = str2real(s) - abs_err = str2real_out - formatted_read_out + to_num_out = to_num(s, to_num_out) + abs_err = to_num_out - formatted_read_out rel_err = abs_err / formatted_read_out if(abs(rel_err) > 10*epsilon(0.0_wp)) then write(*,"('formatted read : ' g0)") formatted_read_out - write(*,"('str2real : ' g0)") str2real_out + write(*,"('to_num : ' g0)") to_num_out write(*,"('difference abs : ' g0)") abs_err write(*,"('difference rel : ' g0 '%')") rel_err * 100 ucheck = .false. From 1f44350479e0f1575bfe024409063e82eff3b336 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Thu, 2 Nov 2023 19:39:41 +0100 Subject: [PATCH 04/74] Fix: elemental attribute lost in refactoring --- src/stdlib_str2num.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 869c56523..47200689c 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -49,7 +49,7 @@ module stdlib_str2num ! String To Number interfaces !--------------------------------------------- - function to_int(s,mold) result(v) + elemental function to_int(s,mold) result(v) ! -- In/out Variables character(*), intent(in) :: s !> input string integer, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface @@ -77,7 +77,7 @@ function to_int_p(s,mold,stat) result(v) if(present(stat)) stat = err end function - function to_float(s,mold) result(r) + elemental function to_float(s,mold) result(r) ! -- In/out Variables character(*), intent(in) :: s !> input string real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface @@ -105,7 +105,7 @@ function to_float_p(s,mold,stat) result(r) if(present(stat)) stat = err end function - function to_double(s,mold) result(r) + elemental function to_double(s,mold) result(r) ! -- In/out Variables character(*), intent(in) :: s !> input string real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface @@ -137,7 +137,7 @@ function to_double_p(s,mold,stat) result(r) ! String To Number Implementations !--------------------------------------------- - subroutine to_int_32(s,v,p,stat) + elemental subroutine to_int_32(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables character(*), intent(in) :: s !> input string @@ -164,7 +164,7 @@ subroutine to_int_32(s,v,p,stat) stat = 0 end subroutine - subroutine to_real_sp(s,v,p,stat) + elemental subroutine to_real_sp(s,v,p,stat) integer, parameter :: wp = sp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables @@ -260,7 +260,7 @@ subroutine to_real_sp(s,v,p,stat) stat = 0 end subroutine - subroutine to_real_dp(s,v,p,stat) + elemental subroutine to_real_dp(s,v,p,stat) integer, parameter :: wp = dp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables From 90a8b987369fbec4bd8c1593648d49f4a8d45961 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Thu, 2 Nov 2023 20:25:14 +0100 Subject: [PATCH 05/74] hexadecimal def of Inf and NaN --- src/stdlib_str2num.f90 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 47200689c..148d80340 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -6,7 +6,7 @@ !> module stdlib_str2num - use iso_fortran_env, only: int32, int64, sp => real32, dp => real64 + use iso_fortran_env, only: sp => real32, dp => real64 implicit none private public :: to_num, to_num_p @@ -174,7 +174,8 @@ elemental subroutine to_real_sp(s,v,p,stat) integer(1), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - real(sp), parameter :: rnan = transfer(int(B'01111111101000000000000000000000',int32), 1._sp) + real(wp), parameter :: rNaN = real(z'7fc00000',wp) + real(wp), parameter :: rInf = real(z'7f800000',wp) ! neginf = real(z'ff800000',wp) integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors integer(kind=ikind), parameter :: nfnb = 37 !> number of fractional number factors integer :: e @@ -199,7 +200,7 @@ elemental subroutine to_real_sp(s,v,p,stat) sign = -1 ; p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*huge(1_wp); return + v = sign*rInf; return else if( iachar(s(p:p)) == NaN ) then v = rNaN; return end if @@ -270,7 +271,8 @@ elemental subroutine to_real_dp(s,v,p,stat) integer(1), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - real(dp), parameter :: rNaN = TRANSFER(9218868437227405313_int64, 1._dp) + real(wp), parameter :: rNaN = real(z'7ff8000000000000',wp) + real(wp), parameter :: rInf = real(z'fff0000000000000',wp) ! neginf = real(z'fff0000000000000',wp) integer(kind=ikind), parameter :: nwnb = 40 !> number of whole number factors integer(kind=ikind), parameter :: nfnb = 64 !> number of fractional number factors integer :: e @@ -295,7 +297,7 @@ elemental subroutine to_real_dp(s,v,p,stat) sign = -1 ; p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*huge(1_wp); return + v = sign*rInf; return else if( iachar(s(p:p)) == NaN ) then v = rNaN; return end if From ceb0b6c3769b63edd8bb7fd7451c210b3c3eadec Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Fri, 3 Nov 2023 08:34:16 +0100 Subject: [PATCH 06/74] Mix real64 exponents for reading real32 --- src/stdlib_str2num.f90 | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 148d80340..1c7adca27 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -179,9 +179,9 @@ elemental subroutine to_real_sp(s,v,p,stat) integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors integer(kind=ikind), parameter :: nfnb = 37 !> number of fractional number factors integer :: e - real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] - real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] - real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] + real(dp), parameter :: whole_number_base(nwnb) = [(10._dp**(nwnb-e),e=1,nwnb)] + real(dp), parameter :: fractional_base(nfnb) = [(10._dp**(-e),e=1,nfnb)] + real(dp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] integer(1) :: sign, sige !> sign of integer number and exponential integer(wp) :: int_wp !> long integer to capture fractional part @@ -253,10 +253,8 @@ elemental subroutine to_real_sp(s,v,p,stat) exp_aux = nwnb-1+resp-sige*max(0,i_exp) if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then v = sign*int_wp*expbase(exp_aux) - else if(exp_aux>nwnb+nfnb) then - v = sign*int_wp*fractional_base(exp_aux-(nwnb+nfnb))*expbase(nwnb+nfnb) else - v = sign*int_wp*10._wp**(sige*max(0,i_exp)-resp) + v = sign*int_wp*10._dp**(sige*max(0,i_exp)-resp+1) end if stat = 0 end subroutine @@ -350,10 +348,8 @@ elemental subroutine to_real_dp(s,v,p,stat) exp_aux = nwnb-1+resp-sige*max(0,i_exp) if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then v = sign*int_wp*expbase(exp_aux) - else if(exp_aux>nwnb+nfnb) then - v = sign*int_wp*fractional_base(exp_aux-(nwnb+nfnb))*expbase(nwnb+nfnb) else - v = sign*int_wp*10._wp**(sige*max(0,i_exp)-resp) + v = sign*int_wp*10._wp**(sige*max(0,i_exp)-resp+1) end if stat = 0 end subroutine From 64dd1c0097a0b6f87840026db940e8583feea8f6 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Fri, 3 Nov 2023 09:37:46 +0100 Subject: [PATCH 07/74] parametrized max depth to read number --- src/stdlib_str2num.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 1c7adca27..330cc5b1c 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -184,7 +184,8 @@ elemental subroutine to_real_sp(s,v,p,stat) real(dp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] integer(1) :: sign, sige !> sign of integer number and exponential - integer(wp) :: int_wp !> long integer to capture fractional part + integer, parameter :: maxdpt = 11 !> Maximum depth to read values on int_wp + integer(dp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part integer :: exp_aux integer(1) :: i, pP, pE, val , resp @@ -208,7 +209,7 @@ elemental subroutine to_real_sp(s,v,p,stat) ! read whole and fractional number in a single integer pP = 127 int_wp = 0 - do i = p, min(10+p-1,len(s)) + do i = p, min(maxdpt+p-1,len(s)) val = iachar(s(i:i))-digit_0 if( val >= 0 .and. val <= 9 ) then int_wp = int_wp*10 + val @@ -279,6 +280,7 @@ elemental subroutine to_real_dp(s,v,p,stat) real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] integer(1) :: sign, sige !> sign of integer number and exponential + integer, parameter :: maxdpt = 19 !> Maximum depth to read values on int_wp integer(wp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part integer :: exp_aux @@ -303,7 +305,7 @@ elemental subroutine to_real_dp(s,v,p,stat) ! read whole and fractional number in a single integer pP = 127 int_wp = 0 - do i = p, min(19+p-1,len(s)) + do i = p, min(maxdpt+p-1,len(s)) val = iachar(s(i:i))-digit_0 if( val >= 0 .and. val <= 9 ) then int_wp = int_wp*10 + val From 3ca78a918aa9b04c57827e0183bc90c03367b859 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Fri, 3 Nov 2023 19:42:31 +0100 Subject: [PATCH 08/74] Bugfix with Inf and NaN --- src/stdlib_str2num.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 330cc5b1c..608a7eb91 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -17,8 +17,8 @@ module stdlib_str2num integer(kind=ikind), parameter :: comma = ichar(',',kind=ikind) - digit_0 integer(kind=ikind), parameter :: minus_sign = ichar('-',kind=ikind) - digit_0 integer(kind=ikind), parameter :: plus_sign = ichar('+',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: Inf = ichar('I',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: NaN = ichar('N',kind=ikind) - digit_0 + integer(kind=ikind), parameter :: Inf = ichar('I',kind=ikind) + integer(kind=ikind), parameter :: NaN = ichar('N',kind=ikind) integer(kind=ikind), parameter :: le = ichar('e',kind=ikind) - digit_0 integer(kind=ikind), parameter :: BE = ichar('E',kind=ikind) - digit_0 integer(kind=ikind), parameter :: ld = ichar('d',kind=ikind) - digit_0 From 36ca79a3ba7ee41a99a5367b8170cc558a564005 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Thu, 23 Nov 2023 21:39:31 +0100 Subject: [PATCH 09/74] use ieee_arithmetic for Inf and NaN --- src/stdlib_str2num.f90 | 21 +++++++++------------ test/string/test_string_to_number.f90 | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 608a7eb91..015a7126c 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -7,6 +7,7 @@ module stdlib_str2num use iso_fortran_env, only: sp => real32, dp => real64 + use ieee_arithmetic implicit none private public :: to_num, to_num_p @@ -174,8 +175,6 @@ elemental subroutine to_real_sp(s,v,p,stat) integer(1), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - real(wp), parameter :: rNaN = real(z'7fc00000',wp) - real(wp), parameter :: rInf = real(z'7f800000',wp) ! neginf = real(z'ff800000',wp) integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors integer(kind=ikind), parameter :: nfnb = 37 !> number of fractional number factors integer :: e @@ -201,9 +200,9 @@ elemental subroutine to_real_sp(s,v,p,stat) sign = -1 ; p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*rInf; return + v = sign*ieee_value(v, ieee_positive_inf); return else if( iachar(s(p:p)) == NaN ) then - v = rNaN; return + v = ieee_value(v, ieee_quiet_nan); return end if !---------------------------------------------- ! read whole and fractional number in a single integer @@ -251,11 +250,11 @@ elemental subroutine to_real_sp(s,v,p,stat) end if end do - exp_aux = nwnb-1+resp-sige*max(0,i_exp) + exp_aux = nwnb-1+resp-sige*i_exp if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then v = sign*int_wp*expbase(exp_aux) else - v = sign*int_wp*10._dp**(sige*max(0,i_exp)-resp+1) + v = sign*int_wp*10._dp**(sige*i_exp-resp+1) end if stat = 0 end subroutine @@ -270,8 +269,6 @@ elemental subroutine to_real_dp(s,v,p,stat) integer(1), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - real(wp), parameter :: rNaN = real(z'7ff8000000000000',wp) - real(wp), parameter :: rInf = real(z'fff0000000000000',wp) ! neginf = real(z'fff0000000000000',wp) integer(kind=ikind), parameter :: nwnb = 40 !> number of whole number factors integer(kind=ikind), parameter :: nfnb = 64 !> number of fractional number factors integer :: e @@ -297,9 +294,9 @@ elemental subroutine to_real_dp(s,v,p,stat) sign = -1 ; p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*rInf; return + v = sign*ieee_value(v, ieee_positive_inf); return else if( iachar(s(p:p)) == NaN ) then - v = rNaN; return + v = ieee_value(v, ieee_quiet_nan); return end if !---------------------------------------------- ! read whole and fractional number in a single integer @@ -347,11 +344,11 @@ elemental subroutine to_real_dp(s,v,p,stat) end if end do - exp_aux = nwnb-1+resp-sige*max(0,i_exp) + exp_aux = nwnb-1+resp-sige*i_exp if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then v = sign*int_wp*expbase(exp_aux) else - v = sign*int_wp*10._wp**(sige*max(0,i_exp)-resp+1) + v = sign*int_wp*10._wp**(sige*i_exp-resp+1) end if stat = 0 end subroutine diff --git a/test/string/test_string_to_number.f90 b/test/string/test_string_to_number.f90 index cf4156c66..32094de19 100644 --- a/test/string/test_string_to_number.f90 +++ b/test/string/test_string_to_number.f90 @@ -106,7 +106,7 @@ logical function ucheck(s) abs_err = to_num_out - formatted_read_out rel_err = abs_err / formatted_read_out - if(abs(rel_err) > 10*epsilon(0.0_wp)) then + if(abs(rel_err) > 0.0_wp) then write(*,"('formatted read : ' g0)") formatted_read_out write(*,"('to_num : ' g0)") to_num_out write(*,"('difference abs : ' g0)") abs_err @@ -205,7 +205,7 @@ logical function ucheck(s) abs_err = to_num_out - formatted_read_out rel_err = abs_err / formatted_read_out - if(abs(rel_err) > 10*epsilon(0.0_wp)) then + if(abs(rel_err) > epsilon(0.0_wp)) then write(*,"('formatted read : ' g0)") formatted_read_out write(*,"('to_num : ' g0)") to_num_out write(*,"('difference abs : ' g0)") abs_err From 3f4e7e3a566151fc469330b38bd30bca3ff6478a Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Mon, 18 Dec 2023 15:25:05 +0100 Subject: [PATCH 10/74] str2num specs proposal --- doc/specs/stdlib_str2num.md | 90 +++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 doc/specs/stdlib_str2num.md diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md new file mode 100644 index 000000000..198735faf --- /dev/null +++ b/doc/specs/stdlib_str2num.md @@ -0,0 +1,90 @@ +--- +title: str2num +--- + +# The `stdlib_str2num` module + +[TOC] + +## `to_num` - conversion of strings to numbers + +### Status + +Experimental + +### Description + +Convert a string or an array of strings to numerical types. + +### Syntax + +`number = [[stdlib_str2num(module):to_num(interface)]](string, mold)` + +### Arguments + +`string`: argument has `intent(in)` and is of type `character(*)`. + +`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32` or `real64`. + +### Return value + +Return a scalar of numerical type (`integer`, `real32` or `real64`). + +### Example + +```fortran +program example_string_to_number + use stdlib_kinds, only: dp + use stdlib_str2num + implicit none + character(:), allocatable :: txt + real(dp) :: x + + txt = ' 8.8541878128e−12 ' + x = to_num( txt , x ) +end program example_random_seed +``` + +## `to_num_p` - conversion of a stream of values in a string to numbers + +### Status + +Experimental + +### Description + +Convert a stream of values in a string to an array of values. + +### Syntax + +`number = [[stdlib_str2num(module):to_num_p(interface)]](string, mold)` + +### Arguments + +`string`: argument has `intent(in)` and is of type `character(*), pointer`. + +`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32` or `real64`. + +### Return value + +Return a scalar of numerical type (`integer`, `real32` or `real64`). + +### Example + +```fortran +program example_str2num + use stdlib_kinds, only: dp + use stdlib_str2num + character(:), allocatable, target :: chain + character(len=:), pointer :: cptr + real(dp), allocatable :: r(:) + integer :: i + + chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" + allocate( r(6) ) + cptr => chain + do i =1, 6 + r(i) = to_num_p( cptr , r(i) ) !> the cptr pointer is shifted within the function + end do +end program +``` \ No newline at end of file From 8c9ba6298e7f1727729dfafd9923448a693d2151 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Mon, 18 Dec 2023 17:12:14 +0100 Subject: [PATCH 11/74] to_num quadruple precision --- src/stdlib_str2num.f90 | 152 +++++++++++++++++++++++++- test/string/test_string_to_number.f90 | 101 ++++++++++++++++- 2 files changed, 251 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 015a7126c..482f9e74f 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -4,9 +4,25 @@ !> This code was modified from https://github.com/jalvesz/Fortran-String-to-Num by Alves Jose !> And was possible thanks to all the discussions in this thread https://fortran-lang.discourse.group/t/faster-string-to-double/ !> +!> Known precisions limits of current proposal : +!> Conversion to double precision is exact up to epsilon(0.0_dp) +!> example: +!> input : 123456.78901234567890123456789012345678901234567890+2 +!> formatted read : 12345678.90123457 +!> to_num : 12345678.90123457 +!> difference abs : 0.1862645149230957E-08 +!> difference rel : 0.1508742584455759E-13% +!> +!> Conversion to quadruple precision can deviate at about 200*epsilon(0.0_qp) +!> example: +!> input : 0.140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125E-443 +!> formatted read : 0.140129846432481707092372958328991608E-443 +!> to_num : 0.140129846432481707092372958328996233E-443 +!> difference abs : 0.4625E-475 +!> difference rel : 0.3300E-029% module stdlib_str2num - use iso_fortran_env, only: sp => real32, dp => real64 + use iso_fortran_env, only: sp => real32, dp => real64, qp => real128 use ieee_arithmetic implicit none private @@ -30,18 +46,21 @@ module stdlib_str2num module procedure to_int module procedure to_float module procedure to_double + module procedure to_quad end interface interface to_num_p module procedure to_int_p module procedure to_float_p module procedure to_double_p + module procedure to_quad_p end interface interface to_num_base module procedure to_int_32 module procedure to_real_sp module procedure to_real_dp + module procedure to_real_qp end interface contains @@ -134,6 +153,34 @@ function to_double_p(s,mold,stat) result(r) if(present(stat)) stat = err end function + function to_quad(s,mold) result(r) + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(qp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + real(qp) :: r !> Output real value + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: stat ! error status + !---------------------------------------------- + call to_num_base(s,r,p,stat) + end function + + function to_quad_p(s,mold,stat) result(r) + ! -- In/out Variables + character(len=:), pointer :: s !> input string + real(qp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + real(qp) :: r !> Output real value + integer(1),intent(inout), optional :: stat + ! -- Internal Variables + integer(1) :: p !> position within the number + integer(1) :: err + !---------------------------------------------- + call to_num_base(s,r,p,err) + p = min( p , len(s) ) + s => s(p:) + if(present(stat)) stat = err + end function + !--------------------------------------------- ! String To Number Implementations !--------------------------------------------- @@ -352,6 +399,109 @@ elemental subroutine to_real_dp(s,v,p,stat) end if stat = 0 end subroutine + + subroutine to_real_qp(s,v,p,stat) + integer, parameter :: wp = qp + !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(wp), intent(inout) :: v !> Output real value + integer(1), intent(out) :: p !> last position within the string + integer(1), intent(out) :: stat !> status upon success or failure to read + + ! -- Internal Variables + integer(kind=ikind), parameter :: nwnb = 50 !> number of whole number factors + integer(kind=ikind), parameter :: nfnb = 64 !> number of fractional number factors + integer :: e + real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] + real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] + real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] + + integer(1) :: sign, sige !> sign of integer number and exponential + integer, parameter :: maxdpt = 19 !> Maximum depth to read values on int_dp + integer(dp) :: int_dp1, int_dp2 !> long integers to capture whole and fractional part + integer :: i_exp !> integer to capture exponent number + integer :: exp_aux + integer(1) :: i, pP, pE, val , resp, icount, aux + !---------------------------------------------- + stat = 23 !> initialize error status with any number > 0 + !---------------------------------------------- + ! Find first non white space + p = mvs2nwsp(s) + !---------------------------------------------- + ! Verify leading negative + sign = 1 + if( iachar(s(p:p)) == minus_sign+digit_0 ) then + sign = -1 ; p = p + 1 + end if + if( iachar(s(p:p)) == Inf ) then + v = sign*ieee_value(v, ieee_positive_inf); return + else if( iachar(s(p:p)) == NaN ) then + v = ieee_value(v, ieee_quiet_nan); return + end if + !---------------------------------------------- + ! read whole and fractional number using two int64 values + pP = 127 + int_dp1 = 0; int_dp2 = 0; icount = 0; aux = 1 + do i = p, min(2*maxdpt+p-1,len(s)) + val = iachar(s(i:i))-digit_0 + if( val >= 0 .and. val <= 9 ) then + icount = icount + 1 + if(icount<=maxdpt)then + int_dp1 = int_dp1*10 + val + else if(icount<2*maxdpt)then + int_dp2 = int_dp2*10 + val + end if + else if( val == period ) then + pP = i; aux = 0 + else + exit + end if + end do + pE = i ! Fix the exponent position + do while( i<=len(s) ) + val = iachar(s(i:i))-digit_0 + if( val < 0 .or. val > 9 ) exit + i = i + 1 + end do + p = i + resp = pE-min(pP,p) ! If no decimal indicator found it is taken as being in the current p position + if( resp <= 0 ) resp = resp+1 + !---------------------------------------------- + ! Get exponential + sige = 1 + if( p= 0 .and. val <= 9) then + i_exp = i_exp*10_ikind + val ; p = p + 1 + else + exit + end if + end do + + exp_aux = nwnb-1+resp-sige*i_exp + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then + if(icount<=maxdpt)then + v = sign*int_dp1*expbase(exp_aux) + else + v = sign*(int_dp1 + int_dp2*fractional_base(maxdpt-1))*expbase(exp_aux-icount+maxdpt) + end if + else + v = sign*(int_dp1 + int_dp2*fractional_base(maxdpt-1))*10._wp**(sige*i_exp-resp+maxdpt+aux) + end if + stat = 0 + end subroutine !--------------------------------------------- ! Internal Utility functions diff --git a/test/string/test_string_to_number.f90 b/test/string/test_string_to_number.f90 index 32094de19..36e749182 100644 --- a/test/string/test_string_to_number.f90 +++ b/test/string/test_string_to_number.f90 @@ -1,5 +1,5 @@ module test_string_to_number - use stdlib_kinds, only: sp, dp + use iso_fortran_env, only: sp=>real32, dp=>real64, qp=>real128 use stdlib_str2num use testdrive, only : new_unittest, unittest_type, error_type, check implicit none @@ -214,6 +214,105 @@ logical function ucheck(s) end if end function end subroutine + + subroutine test_to_quadruple(error) + use stdlib_str2num + type(error_type), allocatable, intent(out) :: error + integer, parameter :: wp = qp + + call check(error, ucheck("1.234")) + if (allocated(error)) return + + call check(error, ucheck("1.E1")) + if (allocated(error)) return + + call check(error, ucheck("1e0")) + if (allocated(error)) return + + call check(error, ucheck("0.1234E0")) + if (allocated(error)) return + + call check(error, ucheck("12.34E0")) + if (allocated(error)) return + + call check(error, ucheck("0.34E2")) + if (allocated(error)) return + + call check(error, ucheck(".34e0")) + if (allocated(error)) return + + call check(error, ucheck("34.E1")) + if (allocated(error)) return + + call check(error, ucheck("-34.5E1")) + if (allocated(error)) return + + call check(error, ucheck("0.0021E10")) + if (allocated(error)) return + + call check(error, ucheck("12.21e-1")) + if (allocated(error)) return + + call check(error, ucheck("12.21e+001 ")) + if (allocated(error)) return + + call check(error, ucheck("-1")) + if (allocated(error)) return + + call check(error, ucheck(" -0.23317260678539647E-01 ")) + if (allocated(error)) return + + call check(error, ucheck(" 2.5647869e-003 "//char(13)//char(10))) + if (allocated(error)) return + + call check(error, ucheck("1.-3")) + if (allocated(error)) return + + call check(error, ucheck("Inf")) + if (allocated(error)) return + + call check(error, ucheck("-Inf")) + if (allocated(error)) return + + call check(error, ucheck("NaN")) + if (allocated(error)) return + + call check(error, ucheck("0.123456789123456789123456789123456789")) + if (allocated(error)) return + + call check(error, ucheck("1234567890123456789012345678901234567890-9") ) + if (allocated(error)) return + + call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) + if (allocated(error)) return + + call check(error, ucheck("0.140129846432481707092372958328991613128026194187651577"//& + & "175706828388979108268586060148663818836212158203125E-44")) + if (allocated(error)) return + + contains + logical function ucheck(s) + character(*), intent(in) :: s + real(wp) :: formatted_read_out + real(wp) :: to_num_out + real(wp) :: abs_err + real(wp) :: rel_err + + ucheck = .true. + read(s,*) formatted_read_out + to_num_out = to_num(s, to_num_out) + abs_err = to_num_out - formatted_read_out + rel_err = abs_err / formatted_read_out + + if(abs(rel_err) > 200*epsilon(0.0_wp)) then + write(*,"('formatted read : ' g0)") formatted_read_out + write(*,"('to_num : ' g0)") to_num_out + write(*,"('difference abs : ' g0)") abs_err + write(*,"('difference rel : ' g0 '%')") rel_err * 100 + ucheck = .false. + end if + end function + end subroutine end module test_string_to_number From 23207665f47d9fd1838e5bc0ff461ae01af162c4 Mon Sep 17 00:00:00 2001 From: Jose Alves Date: Tue, 19 Dec 2023 08:25:08 +0100 Subject: [PATCH 12/74] spec str2num: enrich context information --- doc/specs/stdlib_str2num.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 198735faf..fff9aaf22 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -4,6 +4,8 @@ title: str2num # The `stdlib_str2num` module +This module proposes a function-style interface for string-to-number conversion. It also profits from Fortran's interfaces to implement precision-dependant algorithms to maximize runtime efficiency. + [TOC] ## `to_num` - conversion of strings to numbers @@ -24,7 +26,7 @@ Convert a string or an array of strings to numerical types. `string`: argument has `intent(in)` and is of type `character(*)`. -`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32` or `real64`. +`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32`, `real64` or `real128`. **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. ### Return value @@ -63,7 +65,7 @@ Convert a stream of values in a string to an array of values. `string`: argument has `intent(in)` and is of type `character(*), pointer`. -`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32` or `real64`. +`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32`, `real64` or `real128`. **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. ### Return value @@ -87,4 +89,17 @@ program example_str2num r(i) = to_num_p( cptr , r(i) ) !> the cptr pointer is shifted within the function end do end program -``` \ No newline at end of file +``` + +## Note +The accuracy of the conversion is implementation dependent; it is recommend that implementers guarantee precision down to the last 3 bits. + +**The current implementation has been tested to provide for** : + +`real32` : exact match + +`real64` : precision up-to epsilon(0.0_real64) + +`real128` : precision around 200*epsilon(0.0_real128) + +Where precision refers to the relative difference between `to_num` and `read`. On the other hand, `to_num` provides speed-ups ranging from 4x to >10x compared to the intrinsic `read`. \ No newline at end of file From 318e4ac957a414dd1130c978be8e44b23d1b7246 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:26:29 +0100 Subject: [PATCH 13/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 482f9e74f..6e903045c 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -190,10 +190,10 @@ elemental subroutine to_int_32(s,v,p,stat) ! -- In/out Variables character(*), intent(in) :: s !> input string integer, intent(inout) :: v !> Output real value - integer(1), intent(out) :: p !> position within the number - integer(1), intent(out) :: stat !> status upon succes or failure to read + integer(int8), intent(out) :: p !> position within the number + integer(int8), intent(out) :: stat !> status upon succes or failure to read ! -- Internal Variables - integer(1) :: val + integer(int8) :: val !---------------------------------------------- stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- From 7c5de6ce9be9af611723687d21040e73ebe1dbd7 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:26:50 +0100 Subject: [PATCH 14/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 6e903045c..1f3eb2cb4 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -218,8 +218,8 @@ elemental subroutine to_real_sp(s,v,p,stat) ! -- In/out Variables character(*), intent(in) :: s !> input string real(wp), intent(inout) :: v !> Output real value - integer(1), intent(out) :: p !> last position within the string - integer(1), intent(out) :: stat !> status upon success or failure to read + integer(int8), intent(out) :: p !> last position within the string + integer(int8), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors From abe20e6fa81e38033031ba4d62f95a198499c615 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:27:12 +0100 Subject: [PATCH 15/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 1f3eb2cb4..7eda6c790 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -229,7 +229,7 @@ elemental subroutine to_real_sp(s,v,p,stat) real(dp), parameter :: fractional_base(nfnb) = [(10._dp**(-e),e=1,nfnb)] real(dp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] - integer(1) :: sign, sige !> sign of integer number and exponential + integer(int8) :: sign, sige !> sign of integer number and exponential integer, parameter :: maxdpt = 11 !> Maximum depth to read values on int_wp integer(dp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part From d7dac73502d4e5e3b0bc38fe26c49f42c4e91b13 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:27:30 +0100 Subject: [PATCH 16/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 7eda6c790..e8bdcc9ed 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -234,7 +234,7 @@ elemental subroutine to_real_sp(s,v,p,stat) integer(dp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part integer :: exp_aux - integer(1) :: i, pP, pE, val , resp + integer(int8) :: i, pP, pE, val , resp !---------------------------------------------- stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- From c9fdeeec382bcc8c068b6752fa0233d7735b7bca Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:27:51 +0100 Subject: [PATCH 17/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index e8bdcc9ed..e6b1342b0 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -312,8 +312,8 @@ elemental subroutine to_real_dp(s,v,p,stat) ! -- In/out Variables character(*), intent(in) :: s !> input string real(wp), intent(inout) :: v !> Output real value - integer(1), intent(out) :: p !> last position within the string - integer(1), intent(out) :: stat !> status upon success or failure to read + integer(int8), intent(out) :: p !> last position within the string + integer(int8), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables integer(kind=ikind), parameter :: nwnb = 40 !> number of whole number factors From e73f12ae279c5157d36fc9a606f5312e29725673 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:28:17 +0100 Subject: [PATCH 18/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index e6b1342b0..a555a7d35 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -323,7 +323,7 @@ elemental subroutine to_real_dp(s,v,p,stat) real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] - integer(1) :: sign, sige !> sign of integer number and exponential + integer(int8) :: sign, sige !> sign of integer number and exponential integer, parameter :: maxdpt = 19 !> Maximum depth to read values on int_wp integer(wp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part From 79beacef5e918e224d7d967fb95be07e796b83f3 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:29:51 +0100 Subject: [PATCH 19/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index a555a7d35..96eb0c968 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -189,7 +189,7 @@ elemental subroutine to_int_32(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables character(*), intent(in) :: s !> input string - integer, intent(inout) :: v !> Output real value + integer(int32), intent(inout) :: v !> Output real value integer(int8), intent(out) :: p !> position within the number integer(int8), intent(out) :: stat !> status upon succes or failure to read ! -- Internal Variables From e9604361153450beb4051487ac6583b658ccb602 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:35:11 +0100 Subject: [PATCH 20/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 96eb0c968..e2cdb8518 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -521,7 +521,7 @@ elemental function mvs2nwsp(s) result(p) elemental function mvs2wsp(s) result(p) !> move string to position of the next white space character character(*),intent(in) :: s !> character chain - integer(1) :: p !> position + integer(int8) :: p !> position !---------------------------------------------- p = 1 do while( p Date: Tue, 26 Dec 2023 14:42:59 +0100 Subject: [PATCH 21/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index e2cdb8518..5e3e95d20 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -406,8 +406,8 @@ subroutine to_real_qp(s,v,p,stat) ! -- In/out Variables character(*), intent(in) :: s !> input string real(wp), intent(inout) :: v !> Output real value - integer(1), intent(out) :: p !> last position within the string - integer(1), intent(out) :: stat !> status upon success or failure to read + integer(int8), intent(out) :: p !> last position within the string + integer(int8), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables integer(kind=ikind), parameter :: nwnb = 50 !> number of whole number factors From 798e43de26953410d34ff27c3f32f50afcf353b8 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:43:47 +0100 Subject: [PATCH 22/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index 5e3e95d20..cb24a104c 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -328,7 +328,7 @@ elemental subroutine to_real_dp(s,v,p,stat) integer(wp) :: int_wp !> long integer to capture fractional part integer :: i_exp !> integer to capture whole number part integer :: exp_aux - integer(1) :: i, pP, pE, val , resp + integer(int8) :: i, pP, pE, val , resp !---------------------------------------------- stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- From e0b8da0e0cc48176e86a8e0da6e8a136043db396 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:44:16 +0100 Subject: [PATCH 23/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index cb24a104c..bb179c055 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -417,7 +417,7 @@ subroutine to_real_qp(s,v,p,stat) real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] - integer(1) :: sign, sige !> sign of integer number and exponential + integer(int8) :: sign, sige !> sign of integer number and exponential integer, parameter :: maxdpt = 19 !> Maximum depth to read values on int_dp integer(dp) :: int_dp1, int_dp2 !> long integers to capture whole and fractional part integer :: i_exp !> integer to capture exponent number From 560f1dae58ace19881c3524203867ad7942f0fab Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:44:46 +0100 Subject: [PATCH 24/74] Update src/stdlib_str2num.f90 Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.f90 index bb179c055..68f1e9510 100644 --- a/src/stdlib_str2num.f90 +++ b/src/stdlib_str2num.f90 @@ -422,7 +422,7 @@ subroutine to_real_qp(s,v,p,stat) integer(dp) :: int_dp1, int_dp2 !> long integers to capture whole and fractional part integer :: i_exp !> integer to capture exponent number integer :: exp_aux - integer(1) :: i, pP, pE, val , resp, icount, aux + integer(int8) :: i, pP, pE, val , resp, icount, aux !---------------------------------------------- stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- From e0fa7696df38c368cb5ebdcd919a5ae778dd4997 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 15:10:19 +0100 Subject: [PATCH 25/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index fff9aaf22..5bfae2031 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -37,7 +37,7 @@ Return a scalar of numerical type (`integer`, `real32` or `real64`). ```fortran program example_string_to_number use stdlib_kinds, only: dp - use stdlib_str2num + use stdlib_str2num, only: to_num implicit none character(:), allocatable :: txt real(dp) :: x From 7c72b5cc78bfeeb3cf0c2817f9edc9e5d7139094 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 15:10:54 +0100 Subject: [PATCH 26/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 5bfae2031..478e1430f 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -44,7 +44,7 @@ program example_string_to_number txt = ' 8.8541878128e−12 ' x = to_num( txt , x ) -end program example_random_seed +end program example_string_to_number ``` ## `to_num_p` - conversion of a stream of values in a string to numbers From 9ee4c3658037fcf880ef6ff57a9e0947e06974d3 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 15:11:24 +0100 Subject: [PATCH 27/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 478e1430f..b8805e6cf 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -76,7 +76,7 @@ Return a scalar of numerical type (`integer`, `real32` or `real64`). ```fortran program example_str2num use stdlib_kinds, only: dp - use stdlib_str2num + use stdlib_str2num, only: to_num_p character(:), allocatable, target :: chain character(len=:), pointer :: cptr real(dp), allocatable :: r(:) From a3df7b63de8aee75f47575cd59958f6702bbb42c Mon Sep 17 00:00:00 2001 From: Jeremie Vandenplas Date: Tue, 26 Dec 2023 15:52:45 +0100 Subject: [PATCH 28/74] Support of fypp for stdlib_str2num --- src/CMakeLists.txt | 2 +- src/{stdlib_str2num.f90 => stdlib_str2num.fypp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{stdlib_str2num.f90 => stdlib_str2num.fypp} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fdf49b64..0c2f76c8d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,6 +55,7 @@ set(fppFiles stdlib_math_is_close.fypp stdlib_math_all_close.fypp stdlib_math_diff.fypp + stdlib_str2num.fypp stdlib_string_type.fypp stdlib_string_type_constructor.fypp stdlib_strings_to_string.fypp @@ -82,7 +83,6 @@ set(SRC stdlib_specialfunctions_legendre.f90 stdlib_quadrature_gauss.f90 stdlib_stringlist_type.f90 - stdlib_str2num.f90 ${outFiles} ) diff --git a/src/stdlib_str2num.f90 b/src/stdlib_str2num.fypp similarity index 100% rename from src/stdlib_str2num.f90 rename to src/stdlib_str2num.fypp From ea6560bc4159dbf89f3a69a11cb5505199fb6e10 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Tue, 26 Dec 2023 16:27:47 +0100 Subject: [PATCH 29/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index b8805e6cf..ad7bbbefa 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -92,7 +92,7 @@ end program ``` ## Note -The accuracy of the conversion is implementation dependent; it is recommend that implementers guarantee precision down to the last 3 bits. +The accuracy of the conversion is implementation dependent; it is recommended that implementers guarantee precision down to the last 3 bits. **The current implementation has been tested to provide for** : From ea2ee725c6009a6153cc2fde326a8c810a466e88 Mon Sep 17 00:00:00 2001 From: Jeremie Vandenplas Date: Tue, 26 Dec 2023 17:29:20 +0100 Subject: [PATCH 30/74] add fypp directives for qp --- src/stdlib_str2num.fypp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 68f1e9510..d4a76cf32 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -22,7 +22,7 @@ !> difference rel : 0.3300E-029% module stdlib_str2num - use iso_fortran_env, only: sp => real32, dp => real64, qp => real128 + use stdlib_kinds, only: sp, dp, qp, int8, int32 use ieee_arithmetic implicit none private @@ -46,21 +46,27 @@ module stdlib_str2num module procedure to_int module procedure to_float module procedure to_double +#:if WITH_QP module procedure to_quad +#:endif end interface interface to_num_p module procedure to_int_p module procedure to_float_p module procedure to_double_p +#:if WITH_QP module procedure to_quad_p +#:endif end interface interface to_num_base module procedure to_int_32 module procedure to_real_sp module procedure to_real_dp +#:if WITH_QP module procedure to_real_qp +#:endif end interface contains @@ -153,6 +159,7 @@ module stdlib_str2num if(present(stat)) stat = err end function +#:if WITH_QP function to_quad(s,mold) result(r) ! -- In/out Variables character(*), intent(in) :: s !> input string @@ -180,6 +187,7 @@ module stdlib_str2num s => s(p:) if(present(stat)) stat = err end function +#:endif !--------------------------------------------- ! String To Number Implementations @@ -400,6 +408,7 @@ module stdlib_str2num stat = 0 end subroutine +#:if WITH_QP subroutine to_real_qp(s,v,p,stat) integer, parameter :: wp = qp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent @@ -502,6 +511,7 @@ module stdlib_str2num end if stat = 0 end subroutine +#:endif !--------------------------------------------- ! Internal Utility functions @@ -529,4 +539,4 @@ module stdlib_str2num end do end function -end module stdlib_str2num \ No newline at end of file +end module stdlib_str2num From 1585c10dfad254542bd5db2bb01b5a1372b4c12d Mon Sep 17 00:00:00 2001 From: Jeremie Vandenplas Date: Tue, 26 Dec 2023 17:41:54 +0100 Subject: [PATCH 31/74] addition of fypp directives for some integer procedures --- src/stdlib_str2num.fypp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index d4a76cf32..dae8865f9 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -1,3 +1,4 @@ +#:include "common.fypp" !> The `stdlib_str2num` module provides procedures and interfaces for conversion !> of characters to numerical types. Currently supported: int32, real32 and real64 !> @@ -61,7 +62,9 @@ module stdlib_str2num end interface interface to_num_base - module procedure to_int_32 + #:for k1, t1 in INT_KINDS_TYPES + module procedure to_${k1}$ + #:endfor module procedure to_real_sp module procedure to_real_dp #:if WITH_QP @@ -193,11 +196,12 @@ module stdlib_str2num ! String To Number Implementations !--------------------------------------------- - elemental subroutine to_int_32(s,v,p,stat) + #:for k1, t1 in INT_KINDS_TYPES + elemental subroutine to_${k1}$(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables character(*), intent(in) :: s !> input string - integer(int32), intent(inout) :: v !> Output real value + ${t1}$, intent(out) :: v !> Output real value integer(int8), intent(out) :: p !> position within the number integer(int8), intent(out) :: stat !> status upon succes or failure to read ! -- Internal Variables @@ -219,6 +223,7 @@ module stdlib_str2num end do stat = 0 end subroutine + #:endfor elemental subroutine to_real_sp(s,v,p,stat) integer, parameter :: wp = sp @@ -520,7 +525,7 @@ module stdlib_str2num elemental function mvs2nwsp(s) result(p) !> move string to position of the next non white space character character(*),intent(in) :: s !> character chain - integer(1) :: p !> position + integer(int8) :: p !> position !---------------------------------------------- p = 1 do while( p Date: Tue, 26 Dec 2023 17:48:25 +0100 Subject: [PATCH 32/74] Addition of fypp directives for some integer procedures --- src/stdlib_str2num.fypp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index dae8865f9..930d27fdc 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -44,7 +44,9 @@ module stdlib_str2num integer(kind=ikind), parameter :: LF = 10, CR = 13, WS = 32 interface to_num - module procedure to_int + #:for k1, t1 in INT_KINDS_TYPES + module procedure to_${k1}$ + #:endfor module procedure to_float module procedure to_double #:if WITH_QP @@ -53,7 +55,9 @@ module stdlib_str2num end interface interface to_num_p - module procedure to_int_p + #:for k1, t1 in INT_KINDS_TYPES + module procedure to_${k1}$_p + #:endfor module procedure to_float_p module procedure to_double_p #:if WITH_QP @@ -78,33 +82,35 @@ module stdlib_str2num ! String To Number interfaces !--------------------------------------------- - elemental function to_int(s,mold) result(v) + #:for k1, t1 in INT_KINDS_TYPES + elemental function to_${k1}$(s,mold) result(v) ! -- In/out Variables character(*), intent(in) :: s !> input string - integer, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - integer :: v !> Output integer 32 value + ${t1}$, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + ${t1}$ :: v !> Output integer 32 value ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: stat !> error status + integer(int8) :: p !> position within the number + integer(int8) :: stat !> error status !---------------------------------------------- call to_num_base(s,v,p,stat) end function - function to_int_p(s,mold,stat) result(v) + function to_${k1}$_p(s,mold,stat) result(v) ! -- In/out Variables character(len=:), pointer :: s !> input string - integer, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - integer :: v !> Output integer 32 value - integer(1),intent(inout), optional :: stat + ${t1}$, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface + ${t1}$ :: v !> Output ${t1}$ value + integer(int8),intent(inout), optional :: stat ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: err + integer(int8) :: p !> position within the number + integer(int8) :: err !---------------------------------------------- call to_num_base(s,v,p,err) p = min( p , len(s) ) s => s(p:) if(present(stat)) stat = err end function + #:endfor elemental function to_float(s,mold) result(r) ! -- In/out Variables From ad0bb718ca2bc177d30a8954958e0118cebf3418 Mon Sep 17 00:00:00 2001 From: Jeremie Vandenplas Date: Tue, 26 Dec 2023 18:30:41 +0100 Subject: [PATCH 33/74] fix issue --- src/stdlib_str2num.fypp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 930d27fdc..fd9b241c8 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -23,7 +23,7 @@ !> difference rel : 0.3300E-029% module stdlib_str2num - use stdlib_kinds, only: sp, dp, qp, int8, int32 + use stdlib_kinds, only: sp, dp, qp, int8, int16, int32, int64 use ieee_arithmetic implicit none private @@ -67,7 +67,7 @@ module stdlib_str2num interface to_num_base #:for k1, t1 in INT_KINDS_TYPES - module procedure to_${k1}$ + module procedure to_int_${k1}$ #:endfor module procedure to_real_sp module procedure to_real_dp @@ -203,7 +203,7 @@ module stdlib_str2num !--------------------------------------------- #:for k1, t1 in INT_KINDS_TYPES - elemental subroutine to_${k1}$(s,v,p,stat) + elemental subroutine to_int_${k1}$(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables character(*), intent(in) :: s !> input string From 17d3dae881bbac8d75050027069a1b977b5064b8 Mon Sep 17 00:00:00 2001 From: Jeremie Vandenplas Date: Tue, 26 Dec 2023 18:59:54 +0100 Subject: [PATCH 34/74] support of fypp for test_to_number --- test/string/CMakeLists.txt | 2 ++ ...t_string_to_number.f90 => test_string_to_number.fypp} | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) rename test/string/{test_string_to_number.f90 => test_string_to_number.fypp} (98%) diff --git a/test/string/CMakeLists.txt b/test/string/CMakeLists.txt index f8e3c2323..833471c9e 100644 --- a/test/string/CMakeLists.txt +++ b/test/string/CMakeLists.txt @@ -3,6 +3,7 @@ # Create a list of the files to be preprocessed set(fppFiles test_string_assignment.fypp + test_string_to_number.fypp ) fypp_f90("${fyppFlags}" "${fppFiles}" outFiles) @@ -14,4 +15,5 @@ ADDTEST(string_match) ADDTEST(string_derivedtype_io) ADDTEST(string_functions) ADDTEST(string_strip_chomp) +ADDTEST(string_to_number) ADDTEST(string_to_string) diff --git a/test/string/test_string_to_number.f90 b/test/string/test_string_to_number.fypp similarity index 98% rename from test/string/test_string_to_number.f90 rename to test/string/test_string_to_number.fypp index 36e749182..1f2190f5b 100644 --- a/test/string/test_string_to_number.f90 +++ b/test/string/test_string_to_number.fypp @@ -1,5 +1,5 @@ module test_string_to_number - use iso_fortran_env, only: sp=>real32, dp=>real64, qp=>real128 + use stdlib_kinds, only: sp, dp, qp use stdlib_str2num use testdrive, only : new_unittest, unittest_type, error_type, check implicit none @@ -14,6 +14,9 @@ subroutine collect_string_to_number(testsuite) testsuite = [ & new_unittest("to_float", test_to_float), & new_unittest("to_double", test_to_double) & +#:if WITH_QP + , new_unittest("to_quadruple", test_to_quadruple) & +#:endif ] end subroutine collect_string_to_number @@ -215,6 +218,7 @@ logical function ucheck(s) end function end subroutine +#:if WITH_QP subroutine test_to_quadruple(error) use stdlib_str2num type(error_type), allocatable, intent(out) :: error @@ -313,6 +317,7 @@ logical function ucheck(s) end if end function end subroutine +#:endif end module test_string_to_number @@ -340,4 +345,4 @@ program tester write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" error stop end if -end program \ No newline at end of file +end program From 0b119a28272d4c14c104290f790e439f990bfcbf Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:23:56 +0100 Subject: [PATCH 35/74] Update example/strings/example_str2num.f90 Co-authored-by: Jeremie Vandenplas --- example/strings/example_str2num.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/strings/example_str2num.f90 b/example/strings/example_str2num.f90 index 0d5203f25..c50afdb6f 100644 --- a/example/strings/example_str2num.f90 +++ b/example/strings/example_str2num.f90 @@ -1,6 +1,6 @@ program example_str2num use stdlib_kinds, only: dp - use stdlib_str2num + use stdlib_str2num, only: to_num_p character(:), allocatable, target :: chain character(len=:), pointer :: cptr real(dp), allocatable :: r(:), p(:) From 3029f8ba4c9ac3c8eb23b8cfdbf5030d3b7d0cdd Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:25:44 +0100 Subject: [PATCH 36/74] Update test/string/test_string_to_number.fypp Co-authored-by: Jeremie Vandenplas --- test/string/test_string_to_number.fypp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index 1f2190f5b..730cc03bf 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -21,7 +21,6 @@ contains end subroutine collect_string_to_number subroutine test_to_float(error) - use stdlib_str2num type(error_type), allocatable, intent(out) :: error integer, parameter :: wp = sp From 4aa28eeca53c7b54372d4be0057ea55430612470 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:26:11 +0100 Subject: [PATCH 37/74] Update test/string/test_string_to_number.fypp Co-authored-by: Jeremie Vandenplas --- test/string/test_string_to_number.fypp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index 730cc03bf..cc8007a64 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -119,7 +119,6 @@ contains end subroutine subroutine test_to_double(error) - use stdlib_str2num type(error_type), allocatable, intent(out) :: error integer, parameter :: wp = dp From 688c3a3e411bfa2d1ca097b5839f3c2c209661e7 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:26:38 +0100 Subject: [PATCH 38/74] Update test/string/test_string_to_number.fypp Co-authored-by: Jeremie Vandenplas --- test/string/test_string_to_number.fypp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index cc8007a64..a42923957 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -218,7 +218,6 @@ contains #:if WITH_QP subroutine test_to_quadruple(error) - use stdlib_str2num type(error_type), allocatable, intent(out) :: error integer, parameter :: wp = qp From 182476329bf0cc1b66b5a2cbbcc4d5eba3a7c913 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:26:53 +0100 Subject: [PATCH 39/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index fd9b241c8..d0cdb27aa 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -186,10 +186,10 @@ module stdlib_str2num character(len=:), pointer :: s !> input string real(qp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface real(qp) :: r !> Output real value - integer(1),intent(inout), optional :: stat + integer(int8),intent(inout), optional :: stat ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: err + integer(int8) :: p !> position within the number + integer(int8) :: err !---------------------------------------------- call to_num_base(s,r,p,err) p = min( p , len(s) ) From a18f3582b48cf80424580c79c9d0c7e1a48c33ba Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:33:33 +0100 Subject: [PATCH 40/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index ad7bbbefa..1e84ba6c5 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -65,7 +65,7 @@ Convert a stream of values in a string to an array of values. `string`: argument has `intent(in)` and is of type `character(*), pointer`. -`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32`, `real64` or `real128`. **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. +`mold`: argument has `intent(in)` and is of numerical type. currently: `int8`, `int16`,`int32`, `int64`, `real32`, `real64` or `real128`. **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. ### Return value From 57c3e1969c4810d3623ee06ae3721e522eb6b757 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:46:10 +0100 Subject: [PATCH 41/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index d0cdb27aa..715878070 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -24,7 +24,7 @@ module stdlib_str2num use stdlib_kinds, only: sp, dp, qp, int8, int16, int32, int64 - use ieee_arithmetic + use, intrinsic:: ieee_arithmetic, only: ieee_value, ieee_positive_inf, ieee_quiet_nan implicit none private public :: to_num, to_num_p From 6c20f80a5c4bf2540e39d6392178410f24818bd9 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:46:45 +0100 Subject: [PATCH 42/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 715878070..b151fdc28 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -118,8 +118,8 @@ module stdlib_str2num real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface real(sp) :: r !> Output real value ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: stat ! error status + integer(int8) :: p !> position within the number + integer(int8) :: stat ! error status !---------------------------------------------- call to_num_base(s,r,p,stat) end function From c630fd779c34c2a5fef234bedc1a01ee045d25c2 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:48:07 +0100 Subject: [PATCH 43/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index b151fdc28..f7ceee491 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -129,10 +129,10 @@ module stdlib_str2num character(len=:), pointer :: s !> input string real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface real(sp) :: r !> Output real value - integer(1),intent(inout), optional :: stat + integer(int8), intent(inout), optional :: stat ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: err + integer(int8) :: p !> position within the number + integer(int8) :: err !---------------------------------------------- call to_num_base(s,r,p,err) p = min( p , len(s) ) From 53cb17e61044dd8bf3f4e6cd2c6d37ea80e16f6d Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:48:48 +0100 Subject: [PATCH 44/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index f7ceee491..c00bee10d 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -146,8 +146,8 @@ module stdlib_str2num real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface real(dp) :: r !> Output real value ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: stat ! error status + integer(int8) :: p !> position within the number + integer(int8) :: stat ! error status !---------------------------------------------- call to_num_base(s,r,p,stat) end function From 50174a4c381aba129c72b3adc559172f9ff0e791 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:49:16 +0100 Subject: [PATCH 45/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index c00bee10d..98eef1333 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -157,10 +157,10 @@ module stdlib_str2num character(len=:), pointer :: s !> input string real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface real(dp) :: r !> Output real value - integer(1),intent(inout), optional :: stat + integer(int8),intent(inout), optional :: stat ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: err + integer(int8) :: p !> position within the number + integer(int8) :: err !---------------------------------------------- call to_num_base(s,r,p,err) p = min( p , len(s) ) From 1eb073f0705bffdb442500a139e6b43573854fa3 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:56:58 +0100 Subject: [PATCH 46/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 1e84ba6c5..dad6465b2 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -96,10 +96,10 @@ The accuracy of the conversion is implementation dependent; it is recommended th **The current implementation has been tested to provide for** : -`real32` : exact match +`sp` : exact match -`real64` : precision up-to epsilon(0.0_real64) +`dp` : precision up-to epsilon(0.0_dp) -`real128` : precision around 200*epsilon(0.0_real128) +`qp` : precision around 200*epsilon(0.0_qp) Where precision refers to the relative difference between `to_num` and `read`. On the other hand, `to_num` provides speed-ups ranging from 4x to >10x compared to the intrinsic `read`. \ No newline at end of file From e840761e13b20bc03587640f6777c3e752c500b7 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:06:49 +0100 Subject: [PATCH 47/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index dad6465b2..84256e7b5 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -69,7 +69,7 @@ Convert a stream of values in a string to an array of values. ### Return value -Return a scalar of numerical type (`integer`, `real32` or `real64`). +Return a scalar of numerical type (i.e., `integer` or `real`). ### Example From 96ef3945e25bfaa72b41897052fcf2f29c46d100 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:07:08 +0100 Subject: [PATCH 48/74] Update test/string/test_string_to_number.fypp Co-authored-by: Jeremie Vandenplas --- test/string/test_string_to_number.fypp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index a42923957..ada9a980e 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -1,6 +1,6 @@ module test_string_to_number use stdlib_kinds, only: sp, dp, qp - use stdlib_str2num + use stdlib_str2num, only: to_num use testdrive, only : new_unittest, unittest_type, error_type, check implicit none From 0929792db3a62568871ba02be4dee987a0f84623 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:07:44 +0100 Subject: [PATCH 49/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 84256e7b5..647ac481d 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -30,7 +30,7 @@ Convert a string or an array of strings to numerical types. ### Return value -Return a scalar of numerical type (`integer`, `real32` or `real64`). +Return a scalar of numerical type (i.e., `integer`, or `real`). ### Example From 07389f7edf23a68874ca58892bed586707dc7887 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:13:51 +0100 Subject: [PATCH 50/74] Apply suggestions from code review Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 647ac481d..514c81045 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -26,7 +26,7 @@ Convert a string or an array of strings to numerical types. `string`: argument has `intent(in)` and is of type `character(*)`. -`mold`: argument has `intent(in)` and is of numerical type. currently: `integer`, `real32`, `real64` or `real128`. **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. +`mold`: argument has `intent(in)` and is of numerical type (that is of `integer` or of `real`). **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. ### Return value From 62f055a66617bd3cdd836262b1a98d21e98b0bad Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:15:46 +0100 Subject: [PATCH 51/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 514c81045..cf9137bdd 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -65,7 +65,7 @@ Convert a stream of values in a string to an array of values. `string`: argument has `intent(in)` and is of type `character(*), pointer`. -`mold`: argument has `intent(in)` and is of numerical type. currently: `int8`, `int16`,`int32`, `int64`, `real32`, `real64` or `real128`. **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. +`mold`: argument has `intent(in)` and is of numerical type (currently of `integer` or `real`). **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. ### Return value From 2274af7a0ca3503cdbcb544e1734010c207fc213 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:24:54 +0100 Subject: [PATCH 52/74] Update stdlib_str2num.md Review from Krausler --- doc/specs/stdlib_str2num.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index cf9137bdd..e61071615 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -26,7 +26,7 @@ Convert a string or an array of strings to numerical types. `string`: argument has `intent(in)` and is of type `character(*)`. -`mold`: argument has `intent(in)` and is of numerical type (that is of `integer` or of `real`). **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. +`mold`: argument has `intent(in)` and is of numerical type (that is of `integer` or of `real`). **Note**: The type of the `mold` argument defines the type of the result. ### Return value @@ -65,7 +65,7 @@ Convert a stream of values in a string to an array of values. `string`: argument has `intent(in)` and is of type `character(*), pointer`. -`mold`: argument has `intent(in)` and is of numerical type (currently of `integer` or `real`). **Note**: the mold argument is included to help compilers chose the correct implementation at compile-time. Currently, compilers are not able to disambiguate functions with respect to the left-hand-side of an assignment. +`mold`: argument has `intent(in)` and is of numerical type (currently of `integer` or `real`). **Note**: The type of the `mold` argument defines the type of the result. ### Return value @@ -102,4 +102,4 @@ The accuracy of the conversion is implementation dependent; it is recommended th `qp` : precision around 200*epsilon(0.0_qp) -Where precision refers to the relative difference between `to_num` and `read`. On the other hand, `to_num` provides speed-ups ranging from 4x to >10x compared to the intrinsic `read`. \ No newline at end of file +Where precision refers to the relative difference between `to_num` and `read`. On the other hand, `to_num` provides speed-ups ranging from 4x to >10x compared to the intrinsic `read`. From 2954856d8b80c2144e7b68ee9787be341f087678 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:30:09 +0100 Subject: [PATCH 53/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index e61071615..742cf6bba 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -63,7 +63,7 @@ Convert a stream of values in a string to an array of values. ### Arguments -`string`: argument has `intent(in)` and is of type `character(*), pointer`. +`string`: argument has `intent(in)` and is of type `character(:), pointer`. `mold`: argument has `intent(in)` and is of numerical type (currently of `integer` or `real`). **Note**: The type of the `mold` argument defines the type of the result. From 6fefa64ec2e6d71437e7a223050a0eb6dddc8aba Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:30:58 +0100 Subject: [PATCH 54/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 742cf6bba..c08a42121 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -79,11 +79,10 @@ program example_str2num use stdlib_str2num, only: to_num_p character(:), allocatable, target :: chain character(len=:), pointer :: cptr - real(dp), allocatable :: r(:) + real(dp) :: r(6) integer :: i chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" - allocate( r(6) ) cptr => chain do i =1, 6 r(i) = to_num_p( cptr , r(i) ) !> the cptr pointer is shifted within the function From ab8c7daf1f18d54924ec8ba3e5a39b207216285f Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:31:09 +0100 Subject: [PATCH 55/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index c08a42121..82c72b497 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -84,7 +84,7 @@ program example_str2num chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" cptr => chain - do i =1, 6 + do i = 1, 6 r(i) = to_num_p( cptr , r(i) ) !> the cptr pointer is shifted within the function end do end program From 327a84f9ff4c2a19e7e5fe84e663a1164ee3e391 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 12:20:17 +0100 Subject: [PATCH 56/74] Update stdlib_str2num.fypp Refactor function naming using fypp preprocessing --- src/stdlib_str2num.fypp | 170 +++++++++------------------------------- 1 file changed, 36 insertions(+), 134 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 98eef1333..0ba61b7cc 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -29,51 +29,35 @@ module stdlib_str2num private public :: to_num, to_num_p - integer, parameter :: ikind = selected_int_kind(2) - integer(kind=ikind), parameter :: digit_0 = ichar('0',kind=ikind) - integer(kind=ikind), parameter :: period = ichar('.',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: comma = ichar(',',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: minus_sign = ichar('-',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: plus_sign = ichar('+',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: Inf = ichar('I',kind=ikind) - integer(kind=ikind), parameter :: NaN = ichar('N',kind=ikind) - integer(kind=ikind), parameter :: le = ichar('e',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: BE = ichar('E',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: ld = ichar('d',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: BD = ichar('D',kind=ikind) - digit_0 - integer(kind=ikind), parameter :: LF = 10, CR = 13, WS = 32 + integer(int8), parameter :: digit_0 = ichar('0',int8) + integer(int8), parameter :: period = ichar('.',int8) - digit_0 + integer(int8), parameter :: comma = ichar(',',int8) - digit_0 + integer(int8), parameter :: minus_sign = ichar('-',int8) - digit_0 + integer(int8), parameter :: plus_sign = ichar('+',int8) - digit_0 + integer(int8), parameter :: Inf = ichar('I',int8) + integer(int8), parameter :: NaN = ichar('N',int8) + integer(int8), parameter :: le = ichar('e',int8) - digit_0 + integer(int8), parameter :: BE = ichar('E',int8) - digit_0 + integer(int8), parameter :: ld = ichar('d',int8) - digit_0 + integer(int8), parameter :: BD = ichar('D',int8) - digit_0 + integer(int8), parameter :: LF = 10, CR = 13, WS = 32 interface to_num - #:for k1, t1 in INT_KINDS_TYPES + #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) module procedure to_${k1}$ #:endfor - module procedure to_float - module procedure to_double -#:if WITH_QP - module procedure to_quad -#:endif end interface interface to_num_p - #:for k1, t1 in INT_KINDS_TYPES + #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) module procedure to_${k1}$_p #:endfor - module procedure to_float_p - module procedure to_double_p -#:if WITH_QP - module procedure to_quad_p -#:endif end interface interface to_num_base - #:for k1, t1 in INT_KINDS_TYPES - module procedure to_int_${k1}$ + #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) + module procedure to_${k1}$_base #:endfor - module procedure to_real_sp - module procedure to_real_dp -#:if WITH_QP - module procedure to_real_qp -#:endif end interface contains @@ -82,12 +66,12 @@ module stdlib_str2num ! String To Number interfaces !--------------------------------------------- - #:for k1, t1 in INT_KINDS_TYPES + #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) elemental function to_${k1}$(s,mold) result(v) ! -- In/out Variables character(*), intent(in) :: s !> input string ${t1}$, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - ${t1}$ :: v !> Output integer 32 value + ${t1}$ :: v !> Output ${t1}$ value ! -- Internal Variables integer(int8) :: p !> position within the number integer(int8) :: stat !> error status @@ -110,100 +94,14 @@ module stdlib_str2num s => s(p:) if(present(stat)) stat = err end function - #:endfor - - elemental function to_float(s,mold) result(r) - ! -- In/out Variables - character(*), intent(in) :: s !> input string - real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - real(sp) :: r !> Output real value - ! -- Internal Variables - integer(int8) :: p !> position within the number - integer(int8) :: stat ! error status - !---------------------------------------------- - call to_num_base(s,r,p,stat) - end function - - function to_float_p(s,mold,stat) result(r) - ! -- In/out Variables - character(len=:), pointer :: s !> input string - real(sp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - real(sp) :: r !> Output real value - integer(int8), intent(inout), optional :: stat - ! -- Internal Variables - integer(int8) :: p !> position within the number - integer(int8) :: err - !---------------------------------------------- - call to_num_base(s,r,p,err) - p = min( p , len(s) ) - s => s(p:) - if(present(stat)) stat = err - end function - - elemental function to_double(s,mold) result(r) - ! -- In/out Variables - character(*), intent(in) :: s !> input string - real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - real(dp) :: r !> Output real value - ! -- Internal Variables - integer(int8) :: p !> position within the number - integer(int8) :: stat ! error status - !---------------------------------------------- - call to_num_base(s,r,p,stat) - end function - - function to_double_p(s,mold,stat) result(r) - ! -- In/out Variables - character(len=:), pointer :: s !> input string - real(dp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - real(dp) :: r !> Output real value - integer(int8),intent(inout), optional :: stat - ! -- Internal Variables - integer(int8) :: p !> position within the number - integer(int8) :: err - !---------------------------------------------- - call to_num_base(s,r,p,err) - p = min( p , len(s) ) - s => s(p:) - if(present(stat)) stat = err - end function - -#:if WITH_QP - function to_quad(s,mold) result(r) - ! -- In/out Variables - character(*), intent(in) :: s !> input string - real(qp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - real(qp) :: r !> Output real value - ! -- Internal Variables - integer(1) :: p !> position within the number - integer(1) :: stat ! error status - !---------------------------------------------- - call to_num_base(s,r,p,stat) - end function - - function to_quad_p(s,mold,stat) result(r) - ! -- In/out Variables - character(len=:), pointer :: s !> input string - real(qp), intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface - real(qp) :: r !> Output real value - integer(int8),intent(inout), optional :: stat - ! -- Internal Variables - integer(int8) :: p !> position within the number - integer(int8) :: err - !---------------------------------------------- - call to_num_base(s,r,p,err) - p = min( p , len(s) ) - s => s(p:) - if(present(stat)) stat = err - end function -#:endif + #:endfor !--------------------------------------------- ! String To Number Implementations !--------------------------------------------- #:for k1, t1 in INT_KINDS_TYPES - elemental subroutine to_int_${k1}$(s,v,p,stat) + elemental subroutine to_${k1}$_base(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables character(*), intent(in) :: s !> input string @@ -229,9 +127,10 @@ module stdlib_str2num end do stat = 0 end subroutine + #:endfor - elemental subroutine to_real_sp(s,v,p,stat) + elemental subroutine to_sp_base(s,v,p,stat) integer, parameter :: wp = sp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables @@ -241,9 +140,12 @@ module stdlib_str2num integer(int8), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - integer(kind=ikind), parameter :: nwnb = 39 !> number of whole number factors - integer(kind=ikind), parameter :: nfnb = 37 !> number of fractional number factors + integer(int8), parameter :: nwnb = 39 !> number of whole number factors + integer(int8), parameter :: nfnb = 37 !> number of fractional number factors integer :: e + ! Notice: We use dp here to obtain exact precision for sp. + ! Otherwise errors may appear in comparison to formatted read. + ! See https://github.com/fortran-lang/stdlib/pull/743#issuecomment-1791953430 for more details real(dp), parameter :: whole_number_base(nwnb) = [(10._dp**(nwnb-e),e=1,nwnb)] real(dp), parameter :: fractional_base(nfnb) = [(10._dp**(-e),e=1,nfnb)] real(dp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] @@ -310,7 +212,7 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_ikind + val ; p = p + 1 + i_exp = i_exp*10_int8 + val ; p = p + 1 else exit end if @@ -325,7 +227,7 @@ module stdlib_str2num stat = 0 end subroutine - elemental subroutine to_real_dp(s,v,p,stat) + elemental subroutine to_dp_base(s,v,p,stat) integer, parameter :: wp = dp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables @@ -335,8 +237,8 @@ module stdlib_str2num integer(int8), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - integer(kind=ikind), parameter :: nwnb = 40 !> number of whole number factors - integer(kind=ikind), parameter :: nfnb = 64 !> number of fractional number factors + integer(int8), parameter :: nwnb = 40 !> number of whole number factors + integer(int8), parameter :: nfnb = 64 !> number of fractional number factors integer :: e real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] @@ -404,7 +306,7 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_ikind + val ; p = p + 1 + i_exp = i_exp*10_int8 + val ; p = p + 1 else exit end if @@ -420,7 +322,7 @@ module stdlib_str2num end subroutine #:if WITH_QP - subroutine to_real_qp(s,v,p,stat) + subroutine to_qp_base(s,v,p,stat) integer, parameter :: wp = qp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables @@ -430,8 +332,8 @@ module stdlib_str2num integer(int8), intent(out) :: stat !> status upon success or failure to read ! -- Internal Variables - integer(kind=ikind), parameter :: nwnb = 50 !> number of whole number factors - integer(kind=ikind), parameter :: nfnb = 64 !> number of fractional number factors + integer(int8), parameter :: nwnb = 50 !> number of whole number factors + integer(int8), parameter :: nfnb = 64 !> number of fractional number factors integer :: e real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] @@ -504,7 +406,7 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_ikind + val ; p = p + 1 + i_exp = i_exp*10_int8 + val ; p = p + 1 else exit end if From ad6cd9a173f0f9064ce28745898a7a9b249d9b82 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Mon, 1 Jan 2024 13:02:17 +0100 Subject: [PATCH 57/74] Update stdlib_str2num.fypp Avoid xdp for to_num_base while an implementation is not decided --- src/stdlib_str2num.fypp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 0ba61b7cc..61f0d88fa 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -44,19 +44,25 @@ module stdlib_str2num interface to_num #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) + #:if k1 != "xdp" module procedure to_${k1}$ + #:endif #:endfor end interface interface to_num_p #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) + #:if k1 != "xdp" module procedure to_${k1}$_p + #:endif #:endfor end interface interface to_num_base #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) + #:if k1 != "xdp" module procedure to_${k1}$_base + #:endif #:endfor end interface @@ -67,6 +73,7 @@ module stdlib_str2num !--------------------------------------------- #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) + #:if k1 != "xdp" elemental function to_${k1}$(s,mold) result(v) ! -- In/out Variables character(*), intent(in) :: s !> input string @@ -95,12 +102,14 @@ module stdlib_str2num if(present(stat)) stat = err end function + #:endif #:endfor !--------------------------------------------- ! String To Number Implementations !--------------------------------------------- #:for k1, t1 in INT_KINDS_TYPES + #:if k1 != "xdp" elemental subroutine to_${k1}$_base(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables @@ -128,6 +137,7 @@ module stdlib_str2num stat = 0 end subroutine + #:endif #:endfor elemental subroutine to_sp_base(s,v,p,stat) @@ -322,7 +332,7 @@ module stdlib_str2num end subroutine #:if WITH_QP - subroutine to_qp_base(s,v,p,stat) + elemental subroutine to_qp_base(s,v,p,stat) integer, parameter :: wp = qp !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent ! -- In/out Variables From b00895afb42df9040929145ff26ab3558cf6fc5e Mon Sep 17 00:00:00 2001 From: jalvesz Date: Mon, 1 Jan 2024 15:12:15 +0100 Subject: [PATCH 58/74] fix build with fpm-deployment.sh and fypp --- ci/fpm-deployment.sh | 8 +++++++- test/hashmaps/test_maps.fypp | 1 + test/string/test_string_assignment.fypp | 1 + test/string/test_string_to_number.fypp | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ci/fpm-deployment.sh b/ci/fpm-deployment.sh index c88c3a227..4030ab1b1 100644 --- a/ci/fpm-deployment.sh +++ b/ci/fpm-deployment.sh @@ -9,7 +9,7 @@ destdir="${DESTDIR:-stdlib-fpm}" fypp="${FYPP:-$(which fypp)}" # Arguments for the fypp preprocessor -fyflags="${FYFLAGS:--DMAXRANK=4}" +fyflags="${FYFLAGS:--DMAXRANK=8}" # Number of parallel jobs for preprocessing if [ $(uname) = "Darwin" ]; then @@ -45,6 +45,12 @@ mkdir -p "$destdir/src" "$destdir/test" "$destdir/example" find src -maxdepth 1 -iname "*.fypp" \ | cut -f1 -d. | xargs -P "$njob" -I{} "$fypp" "{}.fypp" "$destdir/{}.f90" $fyflags +find test -name "test_*.fypp" -exec cp {} "$destdir/test/" \; +fyflags="${fyflags} -I src" +find $destdir/test -maxdepth 1 -iname "*.fypp" \ + | cut -f1 -d. | xargs -P "$njob" -I{} "$fypp" "{}.fypp" "{}.f90" $fyflags +find $destdir/test -name "test_*.fypp" -exec rm {} \; + # Collect stdlib source files find src -maxdepth 1 -iname "*.f90" -exec cp {} "$destdir/src/" \; find test -name "test_*.f90" -exec cp {} "$destdir/test/" \; diff --git a/test/hashmaps/test_maps.fypp b/test/hashmaps/test_maps.fypp index cd1e3a4ee..8e8311c96 100644 --- a/test/hashmaps/test_maps.fypp +++ b/test/hashmaps/test_maps.fypp @@ -1,3 +1,4 @@ +#: include "common.fypp" #:set HASH_NAME = ["fnv_1_hasher", "fnv_1a_hasher", "seeded_nmhash32_hasher", "seeded_nmhash32x_hasher", "seeded_water_hasher"] #:set SIZE_NAME = ["16", "256"] module test_stdlib_chaining_maps diff --git a/test/string/test_string_assignment.fypp b/test/string/test_string_assignment.fypp index e64bcc754..eb846e450 100644 --- a/test/string/test_string_assignment.fypp +++ b/test/string/test_string_assignment.fypp @@ -1,3 +1,4 @@ +#: include "common.fypp" ! SPDX-Identifier: MIT module test_string_assignment use testdrive, only : new_unittest, unittest_type, error_type, check diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index ada9a980e..e0fe0257b 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -1,3 +1,4 @@ +#: include "common.fypp" module test_string_to_number use stdlib_kinds, only: sp, dp, qp use stdlib_str2num, only: to_num From 94bf4a8013f20e1e5ab9ecdfcb53f60e5f55e5a2 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Mon, 1 Jan 2024 15:31:32 +0100 Subject: [PATCH 59/74] rewrite test_string_to_number using fypp for real kinds --- test/string/test_string_to_number.fypp | 215 ++----------------------- 1 file changed, 14 insertions(+), 201 deletions(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index e0fe0257b..ed3803a2a 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -13,17 +13,19 @@ contains type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & - new_unittest("to_float", test_to_float), & - new_unittest("to_double", test_to_double) & + new_unittest("to_sp", test_to_sp), & + new_unittest("to_dp", test_to_dp) & #:if WITH_QP - , new_unittest("to_quadruple", test_to_quadruple) & + , new_unittest("to_qp", test_to_qp) & #:endif ] end subroutine collect_string_to_number - subroutine test_to_float(error) + #:for k1, t1 in REAL_KINDS_TYPES + #:if k1 != "xdp" + subroutine test_to_${k1}$(error) type(error_type), allocatable, intent(out) :: error - integer, parameter :: wp = sp + integer, parameter :: wp = ${k1}$ call check(error, ucheck("1.234")) if (allocated(error)) return @@ -109,204 +111,13 @@ contains abs_err = to_num_out - formatted_read_out rel_err = abs_err / formatted_read_out + #:if k1 == "sp" if(abs(rel_err) > 0.0_wp) then - write(*,"('formatted read : ' g0)") formatted_read_out - write(*,"('to_num : ' g0)") to_num_out - write(*,"('difference abs : ' g0)") abs_err - write(*,"('difference rel : ' g0 '%')") rel_err * 100 - ucheck = .false. - end if - end function - end subroutine - - subroutine test_to_double(error) - type(error_type), allocatable, intent(out) :: error - integer, parameter :: wp = dp - - call check(error, ucheck("1.234")) - if (allocated(error)) return - - call check(error, ucheck("1.E1")) - if (allocated(error)) return - - call check(error, ucheck("1e0")) - if (allocated(error)) return - - call check(error, ucheck("0.1234E0")) - if (allocated(error)) return - - call check(error, ucheck("12.34E0")) - if (allocated(error)) return - - call check(error, ucheck("0.34E2")) - if (allocated(error)) return - - call check(error, ucheck(".34e0")) - if (allocated(error)) return - - call check(error, ucheck("34.E1")) - if (allocated(error)) return - - call check(error, ucheck("-34.5E1")) - if (allocated(error)) return - - call check(error, ucheck("0.0021E10")) - if (allocated(error)) return - - call check(error, ucheck("12.21e-1")) - if (allocated(error)) return - - call check(error, ucheck("12.21e+001 ")) - if (allocated(error)) return - - call check(error, ucheck("-1")) - if (allocated(error)) return - - call check(error, ucheck(" -0.23317260678539647E-01 ")) - if (allocated(error)) return - - call check(error, ucheck(" 2.5647869e-003 "//char(13)//char(10))) - if (allocated(error)) return - - call check(error, ucheck("1.-3")) - if (allocated(error)) return - - call check(error, ucheck("Inf")) - if (allocated(error)) return - - call check(error, ucheck("-Inf")) - if (allocated(error)) return - - call check(error, ucheck("NaN")) - if (allocated(error)) return - - call check(error, ucheck("0.123456789123456789123456789123456789")) - if (allocated(error)) return - - call check(error, ucheck("1234567890123456789012345678901234567890-9") ) - if (allocated(error)) return - - call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) - if (allocated(error)) return - - call check(error, ucheck("0.140129846432481707092372958328991613128026194187651577"//& - & "175706828388979108268586060148663818836212158203125E-44")) - if (allocated(error)) return - - contains - logical function ucheck(s) - character(*), intent(in) :: s - real(wp) :: formatted_read_out - real(wp) :: to_num_out - real(wp) :: abs_err - real(wp) :: rel_err - - ucheck = .true. - read(s,*) formatted_read_out - to_num_out = to_num(s, to_num_out) - abs_err = to_num_out - formatted_read_out - rel_err = abs_err / formatted_read_out - + #:elif k1 == "dp" if(abs(rel_err) > epsilon(0.0_wp)) then - write(*,"('formatted read : ' g0)") formatted_read_out - write(*,"('to_num : ' g0)") to_num_out - write(*,"('difference abs : ' g0)") abs_err - write(*,"('difference rel : ' g0 '%')") rel_err * 100 - ucheck = .false. - end if - end function - end subroutine - -#:if WITH_QP - subroutine test_to_quadruple(error) - type(error_type), allocatable, intent(out) :: error - integer, parameter :: wp = qp - - call check(error, ucheck("1.234")) - if (allocated(error)) return - - call check(error, ucheck("1.E1")) - if (allocated(error)) return - - call check(error, ucheck("1e0")) - if (allocated(error)) return - - call check(error, ucheck("0.1234E0")) - if (allocated(error)) return - - call check(error, ucheck("12.34E0")) - if (allocated(error)) return - - call check(error, ucheck("0.34E2")) - if (allocated(error)) return - - call check(error, ucheck(".34e0")) - if (allocated(error)) return - - call check(error, ucheck("34.E1")) - if (allocated(error)) return - - call check(error, ucheck("-34.5E1")) - if (allocated(error)) return - - call check(error, ucheck("0.0021E10")) - if (allocated(error)) return - - call check(error, ucheck("12.21e-1")) - if (allocated(error)) return - - call check(error, ucheck("12.21e+001 ")) - if (allocated(error)) return - - call check(error, ucheck("-1")) - if (allocated(error)) return - - call check(error, ucheck(" -0.23317260678539647E-01 ")) - if (allocated(error)) return - - call check(error, ucheck(" 2.5647869e-003 "//char(13)//char(10))) - if (allocated(error)) return - - call check(error, ucheck("1.-3")) - if (allocated(error)) return - - call check(error, ucheck("Inf")) - if (allocated(error)) return - - call check(error, ucheck("-Inf")) - if (allocated(error)) return - - call check(error, ucheck("NaN")) - if (allocated(error)) return - - call check(error, ucheck("0.123456789123456789123456789123456789")) - if (allocated(error)) return - - call check(error, ucheck("1234567890123456789012345678901234567890-9") ) - if (allocated(error)) return - - call check(error, ucheck("123456.78901234567890123456789012345678901234567890+2") ) - if (allocated(error)) return - - call check(error, ucheck("0.140129846432481707092372958328991613128026194187651577"//& - & "175706828388979108268586060148663818836212158203125E-44")) - if (allocated(error)) return - - contains - logical function ucheck(s) - character(*), intent(in) :: s - real(wp) :: formatted_read_out - real(wp) :: to_num_out - real(wp) :: abs_err - real(wp) :: rel_err - - ucheck = .true. - read(s,*) formatted_read_out - to_num_out = to_num(s, to_num_out) - abs_err = to_num_out - formatted_read_out - rel_err = abs_err / formatted_read_out - + #:elif k1 == "qp" if(abs(rel_err) > 200*epsilon(0.0_wp)) then + #:endif write(*,"('formatted read : ' g0)") formatted_read_out write(*,"('to_num : ' g0)") to_num_out write(*,"('difference abs : ' g0)") abs_err @@ -315,7 +126,9 @@ contains end if end function end subroutine -#:endif + + #:endif + #:endfor end module test_string_to_number From 5d380a05237f0b48fe5d1ff7bc90bff247529873 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Mon, 1 Jan 2024 18:14:17 +0100 Subject: [PATCH 60/74] add support for extended double precision --- src/stdlib_str2num.fypp | 117 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 11 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 61f0d88fa..33c2243ef 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -23,7 +23,7 @@ !> difference rel : 0.3300E-029% module stdlib_str2num - use stdlib_kinds, only: sp, dp, qp, int8, int16, int32, int64 + use stdlib_kinds, only: sp, dp, xdp, qp, int8, int16, int32, int64 use, intrinsic:: ieee_arithmetic, only: ieee_value, ieee_positive_inf, ieee_quiet_nan implicit none private @@ -44,25 +44,19 @@ module stdlib_str2num interface to_num #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) - #:if k1 != "xdp" module procedure to_${k1}$ - #:endif #:endfor end interface interface to_num_p #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) - #:if k1 != "xdp" module procedure to_${k1}$_p - #:endif #:endfor end interface interface to_num_base #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) - #:if k1 != "xdp" module procedure to_${k1}$_base - #:endif #:endfor end interface @@ -73,7 +67,6 @@ module stdlib_str2num !--------------------------------------------- #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) - #:if k1 != "xdp" elemental function to_${k1}$(s,mold) result(v) ! -- In/out Variables character(*), intent(in) :: s !> input string @@ -102,14 +95,12 @@ module stdlib_str2num if(present(stat)) stat = err end function - #:endif #:endfor !--------------------------------------------- ! String To Number Implementations !--------------------------------------------- #:for k1, t1 in INT_KINDS_TYPES - #:if k1 != "xdp" elemental subroutine to_${k1}$_base(s,v,p,stat) !> Return an unsigned 32-bit integer ! -- In/out Variables @@ -137,7 +128,6 @@ module stdlib_str2num stat = 0 end subroutine - #:endif #:endfor elemental subroutine to_sp_base(s,v,p,stat) @@ -331,6 +321,111 @@ module stdlib_str2num stat = 0 end subroutine +#:if WITH_XDP + elemental subroutine to_xdp_base(s,v,p,stat) + integer, parameter :: wp = xdp + !> Sequentially unroll the character and get the sub integers composing the whole number, fraction and exponent + ! -- In/out Variables + character(*), intent(in) :: s !> input string + real(wp), intent(inout) :: v !> Output real value + integer(int8), intent(out) :: p !> last position within the string + integer(int8), intent(out) :: stat !> status upon success or failure to read + + ! -- Internal Variables + integer(int8), parameter :: nwnb = 50 !> number of whole number factors + integer(int8), parameter :: nfnb = 64 !> number of fractional number factors + integer :: e + real(wp), parameter :: whole_number_base(nwnb) = [(10._wp**(nwnb-e),e=1,nwnb)] + real(wp), parameter :: fractional_base(nfnb) = [(10._wp**(-e),e=1,nfnb)] + real(wp), parameter :: expbase(nwnb+nfnb) = [whole_number_base, fractional_base] + + integer(int8) :: sign, sige !> sign of integer number and exponential + integer, parameter :: maxdpt = 19 !> Maximum depth to read values on int_dp + integer(dp) :: int_dp1, int_dp2 !> long integers to capture whole and fractional part + integer :: i_exp !> integer to capture exponent number + integer :: exp_aux + integer(int8) :: i, pP, pE, val , resp, icount, aux + !---------------------------------------------- + stat = 23 !> initialize error status with any number > 0 + !---------------------------------------------- + ! Find first non white space + p = mvs2nwsp(s) + !---------------------------------------------- + ! Verify leading negative + sign = 1 + if( iachar(s(p:p)) == minus_sign+digit_0 ) then + sign = -1 ; p = p + 1 + end if + if( iachar(s(p:p)) == Inf ) then + v = sign*ieee_value(v, ieee_positive_inf); return + else if( iachar(s(p:p)) == NaN ) then + v = ieee_value(v, ieee_quiet_nan); return + end if + !---------------------------------------------- + ! read whole and fractional number using two int64 values + pP = 127 + int_dp1 = 0; int_dp2 = 0; icount = 0; aux = 1 + do i = p, min(2*maxdpt+p-1,len(s)) + val = iachar(s(i:i))-digit_0 + if( val >= 0 .and. val <= 9 ) then + icount = icount + 1 + if(icount<=maxdpt)then + int_dp1 = int_dp1*10 + val + else if(icount<2*maxdpt)then + int_dp2 = int_dp2*10 + val + end if + else if( val == period ) then + pP = i; aux = 0 + else + exit + end if + end do + pE = i ! Fix the exponent position + do while( i<=len(s) ) + val = iachar(s(i:i))-digit_0 + if( val < 0 .or. val > 9 ) exit + i = i + 1 + end do + p = i + resp = pE-min(pP,p) ! If no decimal indicator found it is taken as being in the current p position + if( resp <= 0 ) resp = resp+1 + !---------------------------------------------- + ! Get exponential + sige = 1 + if( p= 0 .and. val <= 9) then + i_exp = i_exp*10_int8 + val ; p = p + 1 + else + exit + end if + end do + + exp_aux = nwnb-1+resp-sige*i_exp + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then + if(icount<=maxdpt)then + v = sign*int_dp1*expbase(exp_aux) + else + v = sign*(int_dp1 + int_dp2*fractional_base(maxdpt-1))*expbase(exp_aux-icount+maxdpt) + end if + else + v = sign*(int_dp1 + int_dp2*fractional_base(maxdpt-1))*10._wp**(sige*i_exp-resp+maxdpt+aux) + end if + stat = 0 + end subroutine +#:endif + #:if WITH_QP elemental subroutine to_qp_base(s,v,p,stat) integer, parameter :: wp = qp From 03c16934abb6a0bc683375cb494f0a78c9eb1767 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Tue, 2 Jan 2024 23:09:59 +0100 Subject: [PATCH 61/74] enable testing for xdp --- test/string/test_string_to_number.fypp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index ed3803a2a..c9f933308 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -22,7 +22,6 @@ contains end subroutine collect_string_to_number #:for k1, t1 in REAL_KINDS_TYPES - #:if k1 != "xdp" subroutine test_to_${k1}$(error) type(error_type), allocatable, intent(out) :: error integer, parameter :: wp = ${k1}$ @@ -115,6 +114,8 @@ contains if(abs(rel_err) > 0.0_wp) then #:elif k1 == "dp" if(abs(rel_err) > epsilon(0.0_wp)) then + #:elif k1 == "xdp" + if(abs(rel_err) > 200*epsilon(0.0_wp)) then #:elif k1 == "qp" if(abs(rel_err) > 200*epsilon(0.0_wp)) then #:endif @@ -126,8 +127,7 @@ contains end if end function end subroutine - - #:endif + #:endfor end module test_string_to_number From 03b4d6070d580e14fe4d1b8efacec4516bc967ac Mon Sep 17 00:00:00 2001 From: jalvesz Date: Tue, 2 Jan 2024 23:14:26 +0100 Subject: [PATCH 62/74] missing xdp kind declaration --- test/string/test_string_to_number.fypp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index c9f933308..9ccd752b4 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -1,6 +1,6 @@ #: include "common.fypp" module test_string_to_number - use stdlib_kinds, only: sp, dp, qp + use stdlib_kinds, only: sp, dp, xdp, qp use stdlib_str2num, only: to_num use testdrive, only : new_unittest, unittest_type, error_type, check implicit none From 44ecace121f626f08f2b7128c303815abe562f2e Mon Sep 17 00:00:00 2001 From: jalvesz Date: Tue, 2 Jan 2024 23:16:16 +0100 Subject: [PATCH 63/74] include xdp test to testsuite --- test/string/test_string_to_number.fypp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/string/test_string_to_number.fypp b/test/string/test_string_to_number.fypp index 9ccd752b4..62a0b5c1a 100644 --- a/test/string/test_string_to_number.fypp +++ b/test/string/test_string_to_number.fypp @@ -17,6 +17,9 @@ contains new_unittest("to_dp", test_to_dp) & #:if WITH_QP , new_unittest("to_qp", test_to_qp) & +#:endif +#:if WITH_XDP + , new_unittest("to_xdp", test_to_xdp) & #:endif ] end subroutine collect_string_to_number From c357fa2a72670ba9ce5900eddbf153abd04f2ed9 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Fri, 5 Jan 2024 08:23:50 +0100 Subject: [PATCH 64/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 82c72b497..8767003f6 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -74,7 +74,7 @@ Return a scalar of numerical type (i.e., `integer` or `real`). ### Example ```fortran -program example_str2num +program example_to_num_p use stdlib_kinds, only: dp use stdlib_str2num, only: to_num_p character(:), allocatable, target :: chain From cbc05b26c1f206a915e751d54ad000159e66a585 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Fri, 5 Jan 2024 08:24:12 +0100 Subject: [PATCH 65/74] Update doc/specs/stdlib_str2num.md Co-authored-by: Jeremie Vandenplas --- doc/specs/stdlib_str2num.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 8767003f6..b6deeedc6 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -87,7 +87,7 @@ program example_to_num_p do i = 1, 6 r(i) = to_num_p( cptr , r(i) ) !> the cptr pointer is shifted within the function end do -end program +end program example_to_num_p ``` ## Note From 026a22c200533ce1e561413d5f9a64706ab4d865 Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 7 Jan 2024 16:08:38 +0100 Subject: [PATCH 66/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 33c2243ef..c3fa8780f 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -1,6 +1,7 @@ #:include "common.fypp" !> The `stdlib_str2num` module provides procedures and interfaces for conversion -!> of characters to numerical types. Currently supported: int32, real32 and real64 +!> of characters to numerical types. Currently supported: `integer` and `real`. +!! ([Specification](../page/specs/stdlib_str2num.html) !> !> This code was modified from https://github.com/jalvesz/Fortran-String-to-Num by Alves Jose !> And was possible thanks to all the discussions in this thread https://fortran-lang.discourse.group/t/faster-string-to-double/ From acefb9960a64ebf39e9302cdf5635faee67f240d Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 7 Jan 2024 16:09:16 +0100 Subject: [PATCH 67/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index c3fa8780f..65cc12f61 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -44,6 +44,10 @@ module stdlib_str2num integer(int8), parameter :: LF = 10, CR = 13, WS = 32 interface to_num + !! version: experimental + !! + !! Conversion of strings to numbers + !! ([Specification](../page/specs/stdlib_str2num.html#to-num-conversion-of-strings-to-numbers)) #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) module procedure to_${k1}$ #:endfor From 89f0015dd325de08a1d56d8ad9f4c43f873bd6ef Mon Sep 17 00:00:00 2001 From: jalvesz <102541118+jalvesz@users.noreply.github.com> Date: Sun, 7 Jan 2024 16:09:31 +0100 Subject: [PATCH 68/74] Update src/stdlib_str2num.fypp Co-authored-by: Jeremie Vandenplas --- src/stdlib_str2num.fypp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 65cc12f61..38a70ea80 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -54,6 +54,10 @@ module stdlib_str2num end interface interface to_num_p + !! version: experimental + !! + !! Conversion of a stream of values in a string to numbers + !! ([Specification](../page/specs/stdlib_str2num.html#to-num-p-conversion-of-a-stream-of-values-in-a-strings-to-numbers)) #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) module procedure to_${k1}$_p #:endfor From fbdf87033ccdd1825e9035344a9d8637a92fff73 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Sun, 7 Jan 2024 16:38:16 +0100 Subject: [PATCH 69/74] autodoc and examples for str2num improvement --- doc/specs/stdlib_str2num.md | 26 ++----------------- ... example_stream_of_strings_to_numbers.f90} | 6 ++--- example/strings/example_string_to_number.f90 | 10 +++++++ 3 files changed, 15 insertions(+), 27 deletions(-) rename example/strings/{example_str2num.f90 => example_stream_of_strings_to_numbers.f90} (81%) create mode 100644 example/strings/example_string_to_number.f90 diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index b6deeedc6..05fb085b0 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -35,16 +35,7 @@ Return a scalar of numerical type (i.e., `integer`, or `real`). ### Example ```fortran -program example_string_to_number - use stdlib_kinds, only: dp - use stdlib_str2num, only: to_num - implicit none - character(:), allocatable :: txt - real(dp) :: x - - txt = ' 8.8541878128e−12 ' - x = to_num( txt , x ) -end program example_string_to_number +{!example/strings/example_string_to_number.f90!} ``` ## `to_num_p` - conversion of a stream of values in a string to numbers @@ -74,20 +65,7 @@ Return a scalar of numerical type (i.e., `integer` or `real`). ### Example ```fortran -program example_to_num_p - use stdlib_kinds, only: dp - use stdlib_str2num, only: to_num_p - character(:), allocatable, target :: chain - character(len=:), pointer :: cptr - real(dp) :: r(6) - integer :: i - - chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" - cptr => chain - do i = 1, 6 - r(i) = to_num_p( cptr , r(i) ) !> the cptr pointer is shifted within the function - end do -end program example_to_num_p +{!example/strings/example_stream_of_strings_to_numbers.f90!} ``` ## Note diff --git a/example/strings/example_str2num.f90 b/example/strings/example_stream_of_strings_to_numbers.f90 similarity index 81% rename from example/strings/example_str2num.f90 rename to example/strings/example_stream_of_strings_to_numbers.f90 index c50afdb6f..c153baedb 100644 --- a/example/strings/example_str2num.f90 +++ b/example/strings/example_stream_of_strings_to_numbers.f90 @@ -1,4 +1,4 @@ -program example_str2num +program example_stream_of_strings_to_numbers use stdlib_kinds, only: dp use stdlib_str2num, only: to_num_p character(:), allocatable, target :: chain @@ -14,9 +14,9 @@ program example_str2num r(i) = to_num_p( cptr , r(i) ) !> the pointer is shifted within the function end do read(chain,*) p - print *, "Reading with to_num" + print *, "Reading with to_num_p" print *, r print *, "Reading with formatted read" print *, p -end program \ No newline at end of file +end program example_stream_of_strings_to_numbers \ No newline at end of file diff --git a/example/strings/example_string_to_number.f90 b/example/strings/example_string_to_number.f90 new file mode 100644 index 000000000..4781a9c6e --- /dev/null +++ b/example/strings/example_string_to_number.f90 @@ -0,0 +1,10 @@ +program example_string_to_number + use stdlib_kinds, only: dp + use stdlib_str2num, only: to_num + implicit none + character(:), allocatable :: txt + real(dp) :: x + + txt = ' 8.8541878128e−12 ' + x = to_num( txt , x ) + end program example_string_to_number \ No newline at end of file From cfb95daf00807d11970e2e5fd334fc56459197fc Mon Sep 17 00:00:00 2001 From: jalvesz Date: Sun, 7 Jan 2024 16:45:25 +0100 Subject: [PATCH 70/74] cmake build str2num examples --- example/strings/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/strings/CMakeLists.txt b/example/strings/CMakeLists.txt index 5ed980646..cbaf4f0f3 100644 --- a/example/strings/CMakeLists.txt +++ b/example/strings/CMakeLists.txt @@ -10,4 +10,5 @@ ADD_EXAMPLE(starts_with) ADD_EXAMPLE(strip) ADD_EXAMPLE(to_string) ADD_EXAMPLE(zfill) -ADD_EXAMPLE(str2num) +ADD_EXAMPLE(string_to_number) +ADD_EXAMPLE(stream_of_strings_to_numbers) \ No newline at end of file From e45b884c798ea03f6647d0af29693d15f030d929 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Sun, 7 Jan 2024 17:24:24 +0100 Subject: [PATCH 71/74] fix fpm processing for maxrank=4 --- ci/fpm-deployment.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ci/fpm-deployment.sh b/ci/fpm-deployment.sh index 4030ab1b1..a8fcaf72e 100644 --- a/ci/fpm-deployment.sh +++ b/ci/fpm-deployment.sh @@ -9,7 +9,8 @@ destdir="${DESTDIR:-stdlib-fpm}" fypp="${FYPP:-$(which fypp)}" # Arguments for the fypp preprocessor -fyflags="${FYFLAGS:--DMAXRANK=8}" +maxrank=4 +fyflags="${FYFLAGS:--DMAXRANK=$maxrank}" # Number of parallel jobs for preprocessing if [ $(uname) = "Darwin" ]; then @@ -34,6 +35,10 @@ prune=( "$destdir/src/f18estop.f90" ) +if [ ${maxrank} -lt 8 ]; then + prune+=("$destdir/test/test_mean_f03.f90") +fi + major=$(cut -d. -f1 VERSION) minor=$(cut -d. -f2 VERSION) patch=$(cut -d. -f3 VERSION) From 7022d83d850e5b7ea37a81757842407e86e9a6c6 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Tue, 9 Jan 2024 20:24:58 +0100 Subject: [PATCH 72/74] rollback prune command in fpm deployment --- ci/fpm-deployment.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ci/fpm-deployment.sh b/ci/fpm-deployment.sh index e6d777e3c..8cc1b245c 100644 --- a/ci/fpm-deployment.sh +++ b/ci/fpm-deployment.sh @@ -34,10 +34,6 @@ prune=( "$destdir/src/f18estop.f90" ) -if [ ${maxrank} -lt 8 ]; then - prune+=("$destdir/test/test_mean_f03.f90") -fi - major=$(cut -d. -f1 VERSION) minor=$(cut -d. -f2 VERSION) patch=$(cut -d. -f3 VERSION) From fa8bdadb00a1a404189479ca9bea5187b51553bf Mon Sep 17 00:00:00 2001 From: jalvesz Date: Mon, 15 Jan 2024 19:36:35 +0100 Subject: [PATCH 73/74] rename functions, remove semicolons --- doc/specs/stdlib_str2num.md | 4 +- .../example_stream_of_strings_to_numbers.f90 | 8 +- src/stdlib_str2num.fypp | 89 ++++++++++++------- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/doc/specs/stdlib_str2num.md b/doc/specs/stdlib_str2num.md index 05fb085b0..47b783cc5 100644 --- a/doc/specs/stdlib_str2num.md +++ b/doc/specs/stdlib_str2num.md @@ -38,7 +38,7 @@ Return a scalar of numerical type (i.e., `integer`, or `real`). {!example/strings/example_string_to_number.f90!} ``` -## `to_num_p` - conversion of a stream of values in a string to numbers +## `to_num_from_stream` - conversion of a stream of values in a string to numbers ### Status @@ -50,7 +50,7 @@ Convert a stream of values in a string to an array of values. ### Syntax -`number = [[stdlib_str2num(module):to_num_p(interface)]](string, mold)` +`number = [[stdlib_str2num(module):to_num_from_stream(interface)]](string, mold)` ### Arguments diff --git a/example/strings/example_stream_of_strings_to_numbers.f90 b/example/strings/example_stream_of_strings_to_numbers.f90 index c153baedb..40845beab 100644 --- a/example/strings/example_stream_of_strings_to_numbers.f90 +++ b/example/strings/example_stream_of_strings_to_numbers.f90 @@ -1,6 +1,6 @@ program example_stream_of_strings_to_numbers use stdlib_kinds, only: dp - use stdlib_str2num, only: to_num_p + use stdlib_str2num, only: to_num_from_stream character(:), allocatable, target :: chain character(len=:), pointer :: cptr real(dp), allocatable :: r(:), p(:) @@ -8,13 +8,13 @@ program example_stream_of_strings_to_numbers chain = " 1.234 1.E1 1e0 0.1234E0 12.21e+001 -34.5E1" allocate( r(6), p(6) ) - !> Example for streamline conversion using `to_num_p` + !> Example for streamline conversion using `to_num_from_stream` cptr => chain do i =1, 6 - r(i) = to_num_p( cptr , r(i) ) !> the pointer is shifted within the function + r(i) = to_num_from_stream( cptr , r(i) ) !> the pointer is shifted within the function end do read(chain,*) p - print *, "Reading with to_num_p" + print *, "Reading with to_num_from_stream" print *, r print *, "Reading with formatted read" print *, p diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index 38a70ea80..f0b7fd709 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -28,7 +28,7 @@ module stdlib_str2num use, intrinsic:: ieee_arithmetic, only: ieee_value, ieee_positive_inf, ieee_quiet_nan implicit none private - public :: to_num, to_num_p + public :: to_num, to_num_from_stream integer(int8), parameter :: digit_0 = ichar('0',int8) integer(int8), parameter :: period = ichar('.',int8) - digit_0 @@ -53,13 +53,13 @@ module stdlib_str2num #:endfor end interface - interface to_num_p + interface to_num_from_stream !! version: experimental !! !! Conversion of a stream of values in a string to numbers !! ([Specification](../page/specs/stdlib_str2num.html#to-num-p-conversion-of-a-stream-of-values-in-a-strings-to-numbers)) #:for k1, t1 in (INT_KINDS_TYPES + REAL_KINDS_TYPES) - module procedure to_${k1}$_p + module procedure to_${k1}$_from_stream #:endfor end interface @@ -88,7 +88,7 @@ module stdlib_str2num call to_num_base(s,v,p,stat) end function - function to_${k1}$_p(s,mold,stat) result(v) + function to_${k1}$_from_stream(s,mold,stat) result(v) ! -- In/out Variables character(len=:), pointer :: s !> input string ${t1}$, intent(in) :: mold !> dummy argument to disambiguate at compile time the generic interface @@ -123,13 +123,14 @@ module stdlib_str2num stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- ! Find first non white space - p = mvs2nwsp(s) + p = shift_to_nonwhitespace(s) !---------------------------------------------- v = 0 do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - v = v*10 + val ; p = p + 1 + v = v*10 + val + p = p + 1 else exit end if @@ -169,17 +170,20 @@ module stdlib_str2num stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- ! Find first non white space - p = mvs2nwsp(s) + p = shift_to_nonwhitespace(s) !---------------------------------------------- ! Verify leading negative sign = 1 if( iachar(s(p:p)) == minus_sign+digit_0 ) then - sign = -1 ; p = p + 1 + sign = -1 + p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*ieee_value(v, ieee_positive_inf); return + v = sign*ieee_value(v, ieee_positive_inf) + return else if( iachar(s(p:p)) == NaN ) then - v = ieee_value(v, ieee_quiet_nan); return + v = ieee_value(v, ieee_quiet_nan) + return end if !---------------------------------------------- ! read whole and fractional number in a single integer @@ -221,7 +225,8 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_int8 + val ; p = p + 1 + i_exp = i_exp*10_int8 + val + p = p + 1 else exit end if @@ -263,17 +268,20 @@ module stdlib_str2num stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- ! Find first non white space - p = mvs2nwsp(s) + p = shift_to_nonwhitespace(s) !---------------------------------------------- ! Verify leading negative sign = 1 if( iachar(s(p:p)) == minus_sign+digit_0 ) then - sign = -1 ; p = p + 1 + sign = -1 + p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*ieee_value(v, ieee_positive_inf); return + v = sign*ieee_value(v, ieee_positive_inf) + return else if( iachar(s(p:p)) == NaN ) then - v = ieee_value(v, ieee_quiet_nan); return + v = ieee_value(v, ieee_quiet_nan) + return end if !---------------------------------------------- ! read whole and fractional number in a single integer @@ -315,7 +323,8 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_int8 + val ; p = p + 1 + i_exp = i_exp*10_int8 + val + p = p + 1 else exit end if @@ -358,22 +367,28 @@ module stdlib_str2num stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- ! Find first non white space - p = mvs2nwsp(s) + p = shift_to_nonwhitespace(s) !---------------------------------------------- ! Verify leading negative sign = 1 if( iachar(s(p:p)) == minus_sign+digit_0 ) then - sign = -1 ; p = p + 1 + sign = -1 + p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*ieee_value(v, ieee_positive_inf); return + v = sign*ieee_value(v, ieee_positive_inf) + return else if( iachar(s(p:p)) == NaN ) then - v = ieee_value(v, ieee_quiet_nan); return + v = ieee_value(v, ieee_quiet_nan) + return end if !---------------------------------------------- ! read whole and fractional number using two int64 values pP = 127 - int_dp1 = 0; int_dp2 = 0; icount = 0; aux = 1 + int_dp1 = 0 + int_dp2 = 0 + icount = 0 + aux = 1 do i = p, min(2*maxdpt+p-1,len(s)) val = iachar(s(i:i))-digit_0 if( val >= 0 .and. val <= 9 ) then @@ -384,7 +399,8 @@ module stdlib_str2num int_dp2 = int_dp2*10 + val end if else if( val == period ) then - pP = i; aux = 0 + pP = i + aux = 0 else exit end if @@ -415,7 +431,8 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_int8 + val ; p = p + 1 + i_exp = i_exp*10_int8 + val + p = p + 1 else exit end if @@ -463,22 +480,28 @@ module stdlib_str2num stat = 23 !> initialize error status with any number > 0 !---------------------------------------------- ! Find first non white space - p = mvs2nwsp(s) + p = shift_to_nonwhitespace(s) !---------------------------------------------- ! Verify leading negative sign = 1 if( iachar(s(p:p)) == minus_sign+digit_0 ) then - sign = -1 ; p = p + 1 + sign = -1 + p = p + 1 end if if( iachar(s(p:p)) == Inf ) then - v = sign*ieee_value(v, ieee_positive_inf); return + v = sign*ieee_value(v, ieee_positive_inf) + return else if( iachar(s(p:p)) == NaN ) then - v = ieee_value(v, ieee_quiet_nan); return + v = ieee_value(v, ieee_quiet_nan) + return end if !---------------------------------------------- ! read whole and fractional number using two int64 values pP = 127 - int_dp1 = 0; int_dp2 = 0; icount = 0; aux = 1 + int_dp1 = 0 + int_dp2 = 0 + icount = 0 + aux = 1 do i = p, min(2*maxdpt+p-1,len(s)) val = iachar(s(i:i))-digit_0 if( val >= 0 .and. val <= 9 ) then @@ -489,7 +512,8 @@ module stdlib_str2num int_dp2 = int_dp2*10 + val end if else if( val == period ) then - pP = i; aux = 0 + pP = i + aux = 0 else exit end if @@ -520,7 +544,8 @@ module stdlib_str2num do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 if( val >= 0 .and. val <= 9) then - i_exp = i_exp*10_int8 + val ; p = p + 1 + i_exp = i_exp*10_int8 + val + p = p + 1 else exit end if @@ -544,7 +569,7 @@ module stdlib_str2num ! Internal Utility functions !--------------------------------------------- - elemental function mvs2nwsp(s) result(p) + elemental function shift_to_nonwhitespace(s) result(p) !> move string to position of the next non white space character character(*),intent(in) :: s !> character chain integer(int8) :: p !> position @@ -555,7 +580,7 @@ module stdlib_str2num end do end function - elemental function mvs2wsp(s) result(p) + elemental function shift_to_whitespace(s) result(p) !> move string to position of the next white space character character(*),intent(in) :: s !> character chain integer(int8) :: p !> position From 3b50f6a83f8134b190767b598b6d4b39170da8b6 Mon Sep 17 00:00:00 2001 From: jalvesz Date: Mon, 15 Jan 2024 19:50:02 +0100 Subject: [PATCH 74/74] add some white spaces --- src/stdlib_str2num.fypp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/stdlib_str2num.fypp b/src/stdlib_str2num.fypp index f0b7fd709..d7deac90c 100644 --- a/src/stdlib_str2num.fypp +++ b/src/stdlib_str2num.fypp @@ -111,7 +111,7 @@ module stdlib_str2num #:for k1, t1 in INT_KINDS_TYPES elemental subroutine to_${k1}$_base(s,v,p,stat) - !> Return an unsigned 32-bit integer + !> Return an ${k1}$ integer ! -- In/out Variables character(*), intent(in) :: s !> input string ${t1}$, intent(out) :: v !> Output real value @@ -128,7 +128,7 @@ module stdlib_str2num v = 0 do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 - if( val >= 0 .and. val <= 9) then + if( val >= 0 .and. val <= 9 ) then v = v*10 + val p = p + 1 else @@ -224,7 +224,7 @@ module stdlib_str2num i_exp = 0 do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 - if( val >= 0 .and. val <= 9) then + if( val >= 0 .and. val <= 9 ) then i_exp = i_exp*10_int8 + val p = p + 1 else @@ -233,7 +233,7 @@ module stdlib_str2num end do exp_aux = nwnb-1+resp-sige*i_exp - if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb ) then v = sign*int_wp*expbase(exp_aux) else v = sign*int_wp*10._dp**(sige*i_exp-resp+1) @@ -322,7 +322,7 @@ module stdlib_str2num i_exp = 0 do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 - if( val >= 0 .and. val <= 9) then + if( val >= 0 .and. val <= 9 ) then i_exp = i_exp*10_int8 + val p = p + 1 else @@ -331,7 +331,7 @@ module stdlib_str2num end do exp_aux = nwnb-1+resp-sige*i_exp - if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb ) then v = sign*int_wp*expbase(exp_aux) else v = sign*int_wp*10._wp**(sige*i_exp-resp+1) @@ -393,9 +393,9 @@ module stdlib_str2num val = iachar(s(i:i))-digit_0 if( val >= 0 .and. val <= 9 ) then icount = icount + 1 - if(icount<=maxdpt)then + if( icount<=maxdpt ) then int_dp1 = int_dp1*10 + val - else if(icount<2*maxdpt)then + else if( icount<2*maxdpt ) then int_dp2 = int_dp2*10 + val end if else if( val == period ) then @@ -430,7 +430,7 @@ module stdlib_str2num i_exp = 0 do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 - if( val >= 0 .and. val <= 9) then + if( val >= 0 .and. val <= 9 ) then i_exp = i_exp*10_int8 + val p = p + 1 else @@ -439,8 +439,8 @@ module stdlib_str2num end do exp_aux = nwnb-1+resp-sige*i_exp - if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then - if(icount<=maxdpt)then + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb ) then + if( icount<=maxdpt ) then v = sign*int_dp1*expbase(exp_aux) else v = sign*(int_dp1 + int_dp2*fractional_base(maxdpt-1))*expbase(exp_aux-icount+maxdpt) @@ -506,9 +506,9 @@ module stdlib_str2num val = iachar(s(i:i))-digit_0 if( val >= 0 .and. val <= 9 ) then icount = icount + 1 - if(icount<=maxdpt)then + if( icount<=maxdpt ) then int_dp1 = int_dp1*10 + val - else if(icount<2*maxdpt)then + else if( icount<2*maxdpt ) then int_dp2 = int_dp2*10 + val end if else if( val == period ) then @@ -543,7 +543,7 @@ module stdlib_str2num i_exp = 0 do while( p<=len(s) ) val = iachar(s(p:p))-digit_0 - if( val >= 0 .and. val <= 9) then + if( val >= 0 .and. val <= 9 ) then i_exp = i_exp*10_int8 + val p = p + 1 else @@ -552,8 +552,8 @@ module stdlib_str2num end do exp_aux = nwnb-1+resp-sige*i_exp - if( exp_aux>0 .and. exp_aux<=nwnb+nfnb) then - if(icount<=maxdpt)then + if( exp_aux>0 .and. exp_aux<=nwnb+nfnb ) then + if( icount<=maxdpt ) then v = sign*int_dp1*expbase(exp_aux) else v = sign*(int_dp1 + int_dp2*fractional_base(maxdpt-1))*expbase(exp_aux-icount+maxdpt) @@ -575,7 +575,7 @@ module stdlib_str2num integer(int8) :: p !> position !---------------------------------------------- p = 1 - do while( p position !---------------------------------------------- p = 1 - do while( p