Skip to content

Commit 3fb8690

Browse files
authored
Use portable formats in savetxt and loadtxt
* Update tests for savetxt and loadtxt; compare exact values, not approximate; remove hardcoded test files * Remove unused variable * Update edit descriptors for savetxt and loadtxt; Small fix in number_of_rows() to make ifort happy * Tests loadtxt with tiny and huge numbers * Compare arrays exactly in test_savetxt_qp.f90
1 parent 87d6dc3 commit 3fb8690

12 files changed

+256
-116
lines changed

src/stdlib_io.fypp

+36-21
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ module stdlib_io
1919
! Private API that is exposed so that we can test it in tests
2020
public :: parse_mode
2121

22+
! Format strings with edit descriptors for each type and kind
23+
character(*), parameter :: &
24+
FMT_INT = '(*(i0,1x))', &
25+
FMT_REAL_SP = '(*(es15.8e2,1x))', &
26+
FMT_REAL_DP = '(*(es24.16e3,1x))', &
27+
FMT_REAL_QP = '(*(es44.35e4,1x))', &
28+
FMT_COMPLEX_SP = '(*(es15.8e2,1x,es15.8e2))', &
29+
FMT_COMPLEX_DP = '(*(es24.16e3,1x,es24.16e3))', &
30+
FMT_COMPLEX_QP = '(*(es44.35e4,1x,es44.35e4))'
31+
2232
interface loadtxt
2333
!! version: experimental
2434
!!
@@ -78,13 +88,22 @@ contains
7888

7989
! determine number of columns
8090
ncol = number_of_columns(s)
91+
#:if 'complex' in t1
92+
ncol = ncol / 2
93+
#:endif
8194

8295
! determine number or rows
83-
nrow = number_of_rows_numeric(s)
96+
nrow = number_of_rows(s)
8497

8598
allocate(d(nrow, ncol))
8699
do i = 1, nrow
87-
read(s, *) d(i, :)
100+
#:if 'real' in t1
101+
read(s, FMT_REAL_${k1}$) d(i, :)
102+
#:elif 'complex' in t1
103+
read(s, FMT_COMPLEX_${k1}$) d(i, :)
104+
#:else
105+
read(s, *) d(i, :)
106+
#:endif
88107
end do
89108
close(s)
90109

@@ -116,7 +135,15 @@ contains
116135
integer :: s, i
117136
s = open(filename, "w")
118137
do i = 1, size(d, 1)
119-
write(s, *) d(i, :)
138+
#:if 'real' in t1
139+
write(s, FMT_REAL_${k1}$) d(i, :)
140+
#:elif 'complex' in t1
141+
write(s, FMT_COMPLEX_${k1}$) d(i, :)
142+
#:elif 'integer' in t1
143+
write(s, FMT_INT) d(i, :)
144+
#:else
145+
write(s, *) d(i, :)
146+
#:endif
120147
end do
121148
close(s)
122149
end subroutine savetxt_${t1[0]}$${k1}$
@@ -147,36 +174,24 @@ contains
147174
end function number_of_columns
148175

149176

150-
integer function number_of_rows_numeric(s) result(nrows)
177+
integer function number_of_rows(s) result(nrows)
151178
!! version: experimental
152179
!!
153-
!! determine number or rows
154-
integer,intent(in)::s
180+
!! Determine the number or rows in a file
181+
integer, intent(in)::s
155182
integer :: ios
156183

157-
real :: r
158-
complex :: z
159-
160184
rewind(s)
161185
nrows = 0
162186
do
163-
read(s, *, iostat=ios) r
187+
read(s, *, iostat=ios)
164188
if (ios /= 0) exit
165189
nrows = nrows + 1
166190
end do
167191

168192
rewind(s)
169193

170-
! If there are no rows of real numbers, it may be that they are complex
171-
if( nrows == 0) then
172-
do
173-
read(s, *, iostat=ios) z
174-
if (ios /= 0) exit
175-
nrows = nrows + 1
176-
end do
177-
rewind(s)
178-
end if
179-
end function number_of_rows_numeric
194+
end function number_of_rows
180195

181196

182197
integer function open(filename, mode, iostat) result(u)
@@ -314,4 +329,4 @@ contains
314329

315330
end function parse_mode
316331

317-
end module
332+
end module stdlib_io

src/tests/io/Makefile.manual

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ PROGS_SRC = test_loadtxt.f90 \
55
test_parse_mode.f90 \
66
test_open.f90
77

8-
CLEAN_FILES = tmp*.dat array*_new.dat io_open.dat io_open.stream
9-
8+
CLEAN_FILES = tmp*.dat io_open.dat io_open.stream
109

1110
include ../Makefile.manual.test.mk

src/tests/io/array1.dat

-4
This file was deleted.

src/tests/io/array2.dat

-4
This file was deleted.

src/tests/io/array3.dat

-16
This file was deleted.

src/tests/io/array4.dat

-3
This file was deleted.

src/tests/io/array5.dat

-2
This file was deleted.

src/tests/io/test_loadtxt.f90

+146-42
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ subroutine collect_loadtxt(testsuite)
1616
testsuite = [ &
1717
new_unittest("loadtxt_int32", test_loadtxt_int32), &
1818
new_unittest("loadtxt_sp", test_loadtxt_sp), &
19+
new_unittest("loadtxt_sp_huge", test_loadtxt_sp_huge), &
20+
new_unittest("loadtxt_sp_tiny", test_loadtxt_sp_tiny), &
1921
new_unittest("loadtxt_dp", test_loadtxt_dp), &
22+
new_unittest("loadtxt_dp_huge", test_loadtxt_dp_huge), &
23+
new_unittest("loadtxt_dp_tiny", test_loadtxt_dp_tiny), &
2024
new_unittest("loadtxt_complex", test_loadtxt_complex) &
2125
]
2226

@@ -27,18 +31,21 @@ subroutine test_loadtxt_int32(error)
2731
!> Error handling
2832
type(error_type), allocatable, intent(out) :: error
2933
integer(int32), allocatable :: input(:,:), expected(:,:)
30-
31-
call loadtxt("array1.dat", input)
32-
call savetxt("array1_new.dat", input)
33-
call loadtxt("array1_new.dat", expected)
34-
call check(error, all(input == expected))
35-
if (allocated(error)) return
36-
37-
call loadtxt("array2.dat", input)
38-
call savetxt("array2_new.dat", input)
39-
call loadtxt("array2_new.dat", expected)
40-
call check(error, all(input == expected))
41-
if (allocated(error)) return
34+
real(sp), allocatable :: harvest(:,:)
35+
integer :: n
36+
37+
allocate(harvest(10,10))
38+
allocate(input(10,10))
39+
allocate(expected(10,10))
40+
41+
do n = 1, 10
42+
call random_number(harvest)
43+
input = int(harvest * 100)
44+
call savetxt('test_int32.txt', input)
45+
call loadtxt('test_int32.txt', expected)
46+
call check(error, all(input == expected))
47+
if (allocated(error)) return
48+
end do
4249

4350
end subroutine test_loadtxt_int32
4451

@@ -47,54 +54,151 @@ subroutine test_loadtxt_sp(error)
4754
!> Error handling
4855
type(error_type), allocatable, intent(out) :: error
4956
real(sp), allocatable :: input(:,:), expected(:,:)
57+
integer :: n
5058

51-
call loadtxt("array3.dat", input)
52-
call savetxt("array3_sp.dat", input)
53-
call loadtxt("array3_sp.dat", expected)
54-
call check(error, all(input == expected))
55-
if (allocated(error)) return
59+
allocate(input(10,10))
60+
allocate(expected(10,10))
5661

57-
call loadtxt("array4.dat", input)
58-
call savetxt("array4_sp.dat", input)
59-
call loadtxt("array4_sp.dat", expected)
60-
call check(error, all(input == expected))
61-
if (allocated(error)) return
62+
do n = 1, 10
63+
call random_number(input)
64+
input = input - 0.5
65+
call savetxt('test_sp.txt', input)
66+
call loadtxt('test_sp.txt', expected)
67+
call check(error, all(input == expected))
68+
if (allocated(error)) return
69+
end do
6270

6371
end subroutine test_loadtxt_sp
6472

6573

74+
subroutine test_loadtxt_sp_huge(error)
75+
!> Error handling
76+
type(error_type), allocatable, intent(out) :: error
77+
real(sp), allocatable :: input(:,:), expected(:,:)
78+
integer :: n
79+
80+
allocate(input(10,10))
81+
allocate(expected(10,10))
82+
83+
do n = 1, 10
84+
call random_number(input)
85+
input = (input - 0.5) * huge(input)
86+
call savetxt('test_sp_huge.txt', input)
87+
call loadtxt('test_sp_huge.txt', expected)
88+
call check(error, all(input == expected))
89+
if (allocated(error)) return
90+
end do
91+
92+
end subroutine test_loadtxt_sp_huge
93+
94+
95+
subroutine test_loadtxt_sp_tiny(error)
96+
!> Error handling
97+
type(error_type), allocatable, intent(out) :: error
98+
real(sp), allocatable :: input(:,:), expected(:,:)
99+
integer :: n
100+
101+
allocate(input(10,10))
102+
allocate(expected(10,10))
103+
104+
do n = 1, 10
105+
call random_number(input)
106+
input = (input - 0.5) * tiny(input)
107+
call savetxt('test_sp_tiny.txt', input)
108+
call loadtxt('test_sp_tiny.txt', expected)
109+
call check(error, all(input == expected))
110+
if (allocated(error)) return
111+
end do
112+
113+
end subroutine test_loadtxt_sp_tiny
114+
115+
66116
subroutine test_loadtxt_dp(error)
67117
!> Error handling
68118
type(error_type), allocatable, intent(out) :: error
69119
real(dp), allocatable :: input(:,:), expected(:,:)
120+
integer :: n
70121

71-
call loadtxt("array3.dat", input)
72-
call savetxt("array3_dp.dat", input)
73-
call loadtxt("array3_dp.dat", expected)
74-
call check(error, all(input == expected))
75-
if (allocated(error)) return
122+
allocate(input(10,10))
123+
allocate(expected(10,10))
76124

77-
call loadtxt("array4.dat", input)
78-
call savetxt("array4_dp.dat", input)
79-
call loadtxt("array4_dp.dat", expected)
80-
call check(error, all(input == expected))
81-
if (allocated(error)) return
125+
do n = 1, 10
126+
call random_number(input)
127+
input = input - 0.5
128+
call savetxt('test_dp.txt', input)
129+
call loadtxt('test_dp.txt', expected)
130+
call check(error, all(input == expected))
131+
if (allocated(error)) return
132+
end do
82133

83134
end subroutine test_loadtxt_dp
84135

85136

86-
subroutine test_loadtxt_complex(error)
87-
!> Error handling
88-
type(error_type), allocatable, intent(out) :: error
89-
complex(dp), allocatable :: input(:,:), expected(:,:)
137+
subroutine test_loadtxt_dp_huge(error)
138+
!> Error handling
139+
type(error_type), allocatable, intent(out) :: error
140+
real(dp), allocatable :: input(:,:), expected(:,:)
141+
integer :: n
142+
143+
allocate(input(10,10))
144+
allocate(expected(10,10))
145+
146+
do n = 1, 10
147+
call random_number(input)
148+
input = (input - 0.5) * huge(input)
149+
call savetxt('test_dp_huge.txt', input)
150+
call loadtxt('test_dp_huge.txt', expected)
151+
call check(error, all(input == expected))
152+
if (allocated(error)) return
153+
end do
154+
155+
end subroutine test_loadtxt_dp_huge
156+
157+
158+
subroutine test_loadtxt_dp_tiny(error)
159+
!> Error handling
160+
type(error_type), allocatable, intent(out) :: error
161+
real(dp), allocatable :: input(:,:), expected(:,:)
162+
integer :: n
163+
164+
allocate(input(10,10))
165+
allocate(expected(10,10))
166+
167+
do n = 1, 10
168+
call random_number(input)
169+
input = (input - 0.5) * tiny(input)
170+
call savetxt('test_dp_tiny.txt', input)
171+
call loadtxt('test_dp_tiny.txt', expected)
172+
call check(error, all(input == expected))
173+
if (allocated(error)) return
174+
end do
90175

91-
call loadtxt("array5.dat", input)
92-
call savetxt("array5_new.dat", input)
93-
call loadtxt("array5_new.dat", expected)
94-
call check(error, all(input == expected))
95-
if (allocated(error)) return
176+
end subroutine test_loadtxt_dp_tiny
96177

97-
end subroutine test_loadtxt_complex
178+
179+
subroutine test_loadtxt_complex(error)
180+
!> Error handling
181+
type(error_type), allocatable, intent(out) :: error
182+
complex(dp), allocatable :: input(:,:), expected(:,:)
183+
real(dp), allocatable :: re(:,:), im(:,:)
184+
integer :: n
185+
186+
allocate(re(10,10))
187+
allocate(im(10,10))
188+
allocate(input(10,10))
189+
allocate(expected(10,10))
190+
191+
do n = 1, 10
192+
call random_number(re)
193+
call random_number(im)
194+
input = cmplx(re, im)
195+
call savetxt('test_complex.txt', input)
196+
call loadtxt('test_complex.txt', expected)
197+
call check(error, all(input == expected))
198+
if (allocated(error)) return
199+
end do
200+
201+
end subroutine test_loadtxt_complex
98202

99203
end module test_loadtxt
100204

0 commit comments

Comments
 (0)