libSUFR
a LIBrary of Some Useful Fortran Routines
All Classes Namespaces Files Functions Variables Pages
numerics.f90
Go to the documentation of this file.
1!> \file numerics.f90 Procedures for numerical operations
2
3
4! Copyright (c) 2002-2025 Marc van der Sluys - Nikhef/Utrecht University - marc.vandersluys.nl
5!
6! This file is part of the libSUFR package,
7! see: http://libsufr.sourceforge.net/
8!
9! This is free software: you can redistribute it and/or modify it under the terms of the European Union
10! Public Licence 1.2 (EUPL 1.2). This software is distributed in the hope that it will be useful, but
11! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12! PURPOSE. See the EU Public Licence for more details. You should have received a copy of the European
13! Union Public Licence along with this code. If not, see <https://www.eupl.eu/1.2/en/>.
14!
15!
16
17
18
19
20!***********************************************************************************************************************************
21!> \brief Procedures for numerical operations
22
24 implicit none
25 save
26
27contains
28
29
30 !*********************************************************************************************************************************
31 !> \brief Return the relative difference between two numbers: dx/<x> - double precision
32 !!
33 !! \param x1 First number
34 !! \param x2 Second number
35 !! \retval reldiff The relative difference
36
37 elemental function reldiff(x1,x2)
38 use sufr_kinds, only: double, dbl
39
40 implicit none
41 real(double), intent(in) :: x1,x2
42 real(double) :: reldiff, xsum,xdiff
43
44 xsum = x1+x2
45 xdiff = x2-x1
46 if(abs(xsum).gt.tiny(xsum)) then
47 reldiff = xdiff / (xsum*0.5_dbl)
48 else ! Can't divide by zero
49 if(abs(xdiff).gt.tiny(xdiff)) then
50 reldiff = xdiff
51 else
52 reldiff = 1.0_dbl ! 0/0
53 end if
54 end if
55
56 end function reldiff
57 !*********************************************************************************************************************************
58
59
60 !*********************************************************************************************************************************
61 !> \brief Return the relative difference between two numbers: dx/<x> - single precision version
62 !!
63 !! \param x1 First number
64 !! \param x2 Second number
65 !! \retval reldiff_sp The relative difference
66
67 elemental function reldiff_sp(x1,x2)
68 implicit none
69 real, intent(in) :: x1,x2
70 real :: reldiff_sp
71
72 reldiff_sp = real(reldiff(dble(x1),dble(x2)))
73
74 end function reldiff_sp
75 !*********************************************************************************************************************************
76
77
78
79
80 !*********************************************************************************************************************************
81 !> \brief Test whether two double-precision variables are equal to better than a given value (default: 2x machine precision)
82 !!
83 !! \param x1 First number
84 !! \param x2 Second number
85 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
86 !!
87 !! \retval deq The two numers are equal (true/false)
88
89 elemental function deq(x1,x2, eps)
90 use sufr_kinds, only: double
91
92 implicit none
93 real(double), intent(in) :: x1,x2
94 real(double), intent(in), optional :: eps
95 real(double) :: leps
96 logical :: deq
97
98 leps = 2*tiny(x1)
99 if(present(eps)) leps = max(leps, abs(eps))
100
101 if(abs(x1-x2).le.leps) then
102 deq = .true.
103 else
104 deq = .false.
105 end if
106
107 end function deq
108 !*********************************************************************************************************************************
109
110 !*********************************************************************************************************************************
111 !> \brief Test whether a double-precision variable is equal to zero better than a given value (default: 2x machine precision)
112 !!
113 !! \param x0 Number to check
114 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
115 !! \retval deq0 The number is equal to 0 (true/false)
116
117 elemental function deq0(x0, eps)
118 use sufr_kinds, only: double
119
120 implicit none
121 real(double), intent(in) :: x0
122 real(double), intent(in), optional :: eps
123 real(double) :: leps
124 logical :: deq0
125
126 leps = 2*tiny(x0)
127 if(present(eps)) leps = max(leps, abs(eps))
128
129 if(abs(x0).le.leps) then
130 deq0 = .true.
131 else
132 deq0 = .false.
133 end if
134
135 end function deq0
136 !*********************************************************************************************************************************
137
138
139 !*********************************************************************************************************************************
140 !> \brief Test whether two single-precision variables are equal to better than a given value (default: 2x machine precision)
141 !!
142 !! \param x1 First number
143 !! \param x2 Second number
144 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
145 !! \retval seq The two numers are equal (true/false)
146
147 elemental function seq(x1,x2, eps)
148 implicit none
149 real, intent(in) :: x1,x2
150 real, intent(in), optional :: eps
151 real :: leps
152 logical :: seq
153
154 leps = 2*tiny(x1)
155 if(present(eps)) leps = max(leps, abs(eps))
156
157 if(abs(x1-x2).le.leps) then
158 seq = .true.
159 else
160 seq = .false.
161 end if
162
163 end function seq
164 !*********************************************************************************************************************************
165
166 !*********************************************************************************************************************************
167 !> \brief Test whether a single-precision variable ais equal to zero better than a given value (default: 2x machine precision)
168 !!
169 !! \param x0 Number to check
170 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
171 !! \retval seq0 The number is equal to 0 (true/false)
172
173 elemental function seq0(x0, eps)
174 implicit none
175 real, intent(in) :: x0
176 real, intent(in), optional :: eps
177 real :: leps
178 logical :: seq0
179
180 leps = 2*tiny(x0)
181 if(present(eps)) leps = max(leps, abs(eps))
182
183 if(abs(x0).le.leps) then
184 seq0 = .true.
185 else
186 seq0 = .false.
187 end if
188
189 end function seq0
190 !*********************************************************************************************************************************
191
192
193 !*********************************************************************************************************************************
194 !> \brief Test whether two double-precision variables are unequal to better than a given value (default: 2x machine precision)
195 !!
196 !! \param x1 First number
197 !! \param x2 Second number
198 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
199 !! \retval dne The two numbers are unequal (true/false)
200
201 elemental function dne(x1,x2, eps)
202 use sufr_kinds, only: double
203 implicit none
204 real(double), intent(in) :: x1,x2
205 real(double), intent(in), optional :: eps
206 logical :: dne
207
208 if(present(eps)) then
209 dne = .not. deq(x1,x2, eps)
210 else
211 dne = .not. deq(x1,x2)
212 end if
213 end function dne
214 !*********************************************************************************************************************************
215
216 !*********************************************************************************************************************************
217 !> \brief Test whether a double-precision variable is unequal to zero better than a given value (default: 2x machine precision)
218 !!
219 !! \param x0 Number to check
220 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
221 !! \retval dne0 The number is unequal to 0 (true/false)
222
223 elemental function dne0(x0, eps)
224 use sufr_kinds, only: double
225 implicit none
226 real(double), intent(in) :: x0
227 real(double), intent(in), optional :: eps
228 logical :: dne0
229
230 if(present(eps)) then
231 dne0 = .not. deq0(x0, eps)
232 else
233 dne0 = .not. deq0(x0)
234 end if
235
236 end function dne0
237 !*********************************************************************************************************************************
238
239
240 !*********************************************************************************************************************************
241 !> \brief Test whether two single-precision variables are unequal to better than a given value (default: 2x machine precision)
242 !!
243 !! \param x1 First number
244 !! \param x2 Second number
245 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
246 !! \retval sne The two numers are unequal (true/false)
247
248 elemental function sne(x1,x2, eps)
249 implicit none
250 real, intent(in) :: x1,x2
251 real, intent(in), optional :: eps
252 logical :: sne
253
254 if(present(eps)) then
255 sne = .not. seq(x1,x2, eps)
256 else
257 sne = .not. seq(x1,x2)
258 end if
259
260 end function sne
261 !*********************************************************************************************************************************
262
263 !*********************************************************************************************************************************
264 !> \brief Test whether a single-precision variable is unequal to zero better than a given value (default: 2x machine precision)
265 !!
266 !! \param x0 Number to check
267 !! \param eps Maximum absolute difference allowed (optional - default: twice the machine precision)
268 !! \retval sne0 The number is unequal to 0 (true/false)
269
270 elemental function sne0(x0, eps)
271 implicit none
272 real, intent(in) :: x0
273 real, intent(in), optional :: eps
274 logical :: sne0
275
276 if(present(eps)) then
277 sne0 = .not. seq0(x0, eps)
278 else
279 sne0 = .not. seq0(x0)
280 end if
281
282 end function sne0
283 !*********************************************************************************************************************************
284
285
286
287
288
289 !*********************************************************************************************************************************
290 !> \brief Test whether a double-precision variable is (+/-) infinite
291 !!
292 !! \param x0 Number to check
293 !! \retval isinf The number is infinite (true/false)
294
295 elemental function isinf(x0)
296 use sufr_kinds, only: double
297 implicit none
298 real(double), intent(in) :: x0
299 logical :: isinf
300
301 isinf = x0.gt.huge(x0) .or. x0.lt.-huge(x0)
302 end function isinf
303 !*********************************************************************************************************************************
304
305 !*********************************************************************************************************************************
306 !> \brief Test whether a single-precision variable is (+/-) infinite
307 !!
308 !! \param x0 Number to check
309 !! \retval sisinf The number is infinite (true/false)
310
311 elemental function sisinf(x0)
312 implicit none
313 real, intent(in) :: x0
314 logical :: sisinf
315
316 sisinf = x0.gt.huge(x0) .or. x0.lt.-huge(x0)
317 end function sisinf
318 !*********************************************************************************************************************************
319
320
321 !*********************************************************************************************************************************
322 !> \brief Test whether a double-precision variable is not a number (NaN)
323 !!
324 !! \param x0 Number to check
325 !! \retval isanan The value is a NaN (true/false)
326
327 elemental function isanan(x0)
328 use sufr_kinds, only: double
329 implicit none
330 real(double), intent(in) :: x0
331 logical :: isanan
332
333 isanan = .not. (x0.le.x0 .or. x0.ge.x0)
334 end function isanan
335 !*********************************************************************************************************************************
336
337 !*********************************************************************************************************************************
338 !> \brief Test whether a single-precision variable is not a number (NaN)
339 !!
340 !! \param x0 Number to check
341 !! \retval sisanan The value is a NaN (true/false)
342
343 elemental function sisanan(x0)
344 implicit none
345 real, intent(in) :: x0
346 logical :: sisanan
347
348 sisanan = .not. (x0.le.x0 .or. x0.ge.x0)
349 end function sisanan
350 !*********************************************************************************************************************************
351
352
353 !*********************************************************************************************************************************
354 !> \brief Test whether a double-precision variable is normal (not +/- Inf, not NaN)
355 !!
356 !! \param x0 Number to check
357 !! \retval isnormal This is a normal value (true/false)
358
359 elemental function isnormal(x0)
360 use sufr_kinds, only: double
361 implicit none
362 real(double), intent(in) :: x0
363 logical :: isnormal
364
365 isnormal = .not.(isinf(x0) .or. isanan(x0))
366 end function isnormal
367 !*********************************************************************************************************************************
368
369 !*********************************************************************************************************************************
370 !> \brief Test whether a single-precision variable is normal (not +/- Inf, not NaN)
371 !!
372 !! \param x0 Number to check
373 !! \retval sisnormal This is a normal value (true/false)
374
375 elemental function sisnormal(x0)
376 implicit none
377 real, intent(in) :: x0
378 logical :: sisnormal
379
380 sisnormal = .not.(sisinf(x0) .or. sisanan(x0))
381 end function sisnormal
382 !*********************************************************************************************************************************
383
384
385
386
387
388 !*********************************************************************************************************************************
389 !> \brief Determine plot ranges from data arrays in x and y, and relative margins
390 !!
391 !! \param plx Array contaiting x values
392 !! \param ply Array contaiting y values
393 !! \param ddx Relative margin in x
394 !! \param ddy Relative margin in y
395 !!
396 !! \param xmin Minimum of plot range in x (output)
397 !! \param xmax Maximum of plot range in x (output)
398 !! \param ymin Minimum of plot range in y (output)
399 !! \param ymax Maximum of plot range in y (output)
400 !!
401 !! \param dx Range width of x (xmax-xmin; output, optional)
402 !! \param dy Range wifth of y (ymax-ymin; output, optional)
403
404 pure subroutine plot_ranges(plx,ply, ddx,ddy, xmin,xmax, ymin,ymax, dx,dy)
405 use sufr_kinds, only: double
406
407 implicit none
408 real(double), intent(in) :: plx(:),ply(:), ddx,ddy
409 real(double), intent(out) :: xmin,xmax, ymin,ymax
410 real(double), intent(out), optional :: dx,dy
411 real(double) :: dx1,dy1
412
413 xmin = minval(plx)
414 xmax = maxval(plx)
415
416 if(deq(xmin,xmax)) then
417 xmin = xmin * (1.d0-ddx)
418 xmax = xmax * (1.d0+ddx)
419 else
420 dx1 = xmax - xmin
421 xmin = xmin - dx1*ddx
422 xmax = xmax + dx1*ddx
423 end if
424
425
426 ymin = minval(ply)
427 ymax = maxval(ply)
428
429 if(deq(ymin,ymax)) then
430 ymin = ymin * (1.d0-ddy)
431 ymax = ymax * (1.d0+ddy)
432 else
433 dy1 = ymax - ymin
434 ymin = ymin - dy1*ddy
435 ymax = ymax + dy1*ddy
436 end if
437
438 ! Optional return values:
439 if(present(dx)) dx = xmax - xmin
440 if(present(dy)) dy = ymax - ymin
441
442 end subroutine plot_ranges
443 !*********************************************************************************************************************************
444
445
446 !*********************************************************************************************************************************
447 !> \brief A modulo function to wrap array indices properly in Fortran ([1,N], rather than [0,N-1])
448 !!
449 !! Since array indices in Fortran run from 1 to N, and the mod() function returns 0 to N-1 which can be used as an array
450 !! index directly in e.g. C, mod1() provides that service in Fortran.
451 !!
452 !! \param number Number to take the modulo of
453 !! \param period Period to wrap around
454 !!
455 !! \retval mod1 Modulo of the given number with the given period
456
457 elemental function mod1(number, period)
458 implicit none
459 integer, intent(in) :: number, period
460 integer :: mod1
461
462 mod1 = mod(number-1+period, period) + 1
463
464 end function mod1
465 !*********************************************************************************************************************************
466
467
468
469 !*********************************************************************************************************************************
470 !> \brief Compute the greatest common divisor (GCD) of two positive integers using the Euclidean algoritm
471 !!
472 !! \param a Positive integer 1
473 !! \param b Positive integer 2
474 !!
475 !! \retval gcd2 The GCD of the two integers
476 !!
477 !! \see https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations
478
479 function gcd2(a,b)
481 implicit none
482 integer, intent(in) :: a, b
483 integer :: gcd2, la,lb,rem
484
485 if(min(a,b).le.0) call quit_program_error('gcd2(): the two integers must be positive ',1)
486
487 ! Don't change the input parameters:
488 la = a
489 lb = b
490
491 if(la.lt.lb) call swapint(la,lb) ! Ensure a >= b
492
493 do
494 rem = mod(la, lb) ! Compute remainder
495 if(rem == 0) exit ! No remainder: we have finished
496
497 la = lb
498 lb = rem
499 end do
500
501 gcd2 = lb
502
503 end function gcd2
504 !*********************************************************************************************************************************
505
506
507 !*********************************************************************************************************************************
508 !> \brief Computes the greatest common divisor (GCD) for an array of positive integers using the Euclidean algoritm
509 !!
510 !! \param array The array of positive integers
511 !!
512 !! \retval gcd The GCD of the integers
513 !!
514 !! \note This function uses gcd2() iteratively
515 !!
516 !! \see https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations
517
518
519 function gcd(array)
521 implicit none
522 integer, intent(in) :: array(:)
523 integer :: gcd, it
524
525 if(minval(array).le.0) call quit_program_error('gcd(): all integers must be positive ',1)
526
527 gcd = maxval(array)
528 do it=1,size(array)
529 gcd = gcd2(array(it),gcd)
530 end do
531
532 end function gcd
533 !*********************************************************************************************************************************
534
535 !*********************************************************************************************************************************
536 !> \brief Computes the least common multiplier (LCM) for an array of positive integers
537 !!
538 !! \param array The array of positive integers
539 !!
540 !! \retval lcm The LCM of the integers
541 !!
542 !! \see https://en.wikipedia.org/wiki/Least_common_multiple#A_simple_algorithm
543
544 function lcm(array)
546 implicit none
547 integer, intent(in) :: array(:)
548 integer :: lcm, larray(size(array)), in
549
550 if(minval(array).le.0) call quit_program_error('lcm(): all integers must be positive ',1)
551
552 larray = array
553 do
554 if(minval(larray).eq.maxval(larray)) exit ! All values are equal, we have finished!
555
556 in = minval(minloc(larray)) ! Index of the (first) smallest value in the array
557 larray(in) = larray(in) + array(in)
558 end do
559
560 lcm = larray(1)
561 end function lcm
562 !*********************************************************************************************************************************
563
564
565end module sufr_numerics
566!***********************************************************************************************************************************
567
Provides kinds and related constants/routines.
Definition kinds.f90:26
integer, parameter double
Double-precision float. Precision = 15, range = 307.
Definition kinds.f90:35
integer, parameter dbl
Double-precision float. Precision = 15, range = 307.
Definition kinds.f90:36
Procedures for numerical operations.
Definition numerics.f90:23
elemental logical function seq0(x0, eps)
Test whether a single-precision variable ais equal to zero better than a given value (default: 2x mac...
Definition numerics.f90:174
elemental logical function dne(x1, x2, eps)
Test whether two double-precision variables are unequal to better than a given value (default: 2x mac...
Definition numerics.f90:202
elemental logical function seq(x1, x2, eps)
Test whether two single-precision variables are equal to better than a given value (default: 2x machi...
Definition numerics.f90:148
elemental logical function isnormal(x0)
Test whether a double-precision variable is normal (not +/- Inf, not NaN)
Definition numerics.f90:360
elemental integer function mod1(number, period)
A modulo function to wrap array indices properly in Fortran ([1,N], rather than [0,...
Definition numerics.f90:458
elemental real(double) function reldiff(x1, x2)
Return the relative difference between two numbers: dx/<x> - double precision.
Definition numerics.f90:38
elemental logical function sisanan(x0)
Test whether a single-precision variable is not a number (NaN)
Definition numerics.f90:344
elemental logical function sisnormal(x0)
Test whether a single-precision variable is normal (not +/- Inf, not NaN)
Definition numerics.f90:376
elemental logical function isanan(x0)
Test whether a double-precision variable is not a number (NaN)
Definition numerics.f90:328
integer function gcd2(a, b)
Compute the greatest common divisor (GCD) of two positive integers using the Euclidean algoritm.
Definition numerics.f90:480
elemental logical function dne0(x0, eps)
Test whether a double-precision variable is unequal to zero better than a given value (default: 2x ma...
Definition numerics.f90:224
elemental logical function deq0(x0, eps)
Test whether a double-precision variable is equal to zero better than a given value (default: 2x mach...
Definition numerics.f90:118
elemental logical function sisinf(x0)
Test whether a single-precision variable is (+/-) infinite.
Definition numerics.f90:312
elemental logical function isinf(x0)
Test whether a double-precision variable is (+/-) infinite.
Definition numerics.f90:296
elemental logical function sne(x1, x2, eps)
Test whether two single-precision variables are unequal to better than a given value (default: 2x mac...
Definition numerics.f90:249
pure subroutine plot_ranges(plx, ply, ddx, ddy, xmin, xmax, ymin, ymax, dx, dy)
Determine plot ranges from data arrays in x and y, and relative margins.
Definition numerics.f90:405
elemental logical function deq(x1, x2, eps)
Test whether two double-precision variables are equal to better than a given value (default: 2x machi...
Definition numerics.f90:90
elemental real function reldiff_sp(x1, x2)
Return the relative difference between two numbers: dx/<x> - single precision version.
Definition numerics.f90:68
elemental logical function sne0(x0, eps)
Test whether a single-precision variable is unequal to zero better than a given value (default: 2x ma...
Definition numerics.f90:271
integer function lcm(array)
Computes the least common multiplier (LCM) for an array of positive integers.
Definition numerics.f90:545
integer function gcd(array)
Computes the greatest common divisor (GCD) for an array of positive integers using the Euclidean algo...
Definition numerics.f90:520
System-related procedures.
Definition system.f90:23
subroutine quit_program_error(message, status)
Print an error message to StdErr and stop the execution of the current program.
Definition system.f90:78
pure subroutine swapint(int1, int2)
Swap two integer variables.
Definition system.f90:994