Skip to content

Commit d395740

Browse files
committed
add is_close in stdlib_math.
1 parent 590adbe commit d395740

File tree

8 files changed

+170
-12
lines changed

8 files changed

+170
-12
lines changed

doc/specs/stdlib_math.md

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,25 +275,25 @@ program demo_logspace_rstart_cbase
275275
276276
end program demo_logspace_rstart_cbase
277277
```
278-
## `arange`
278+
### `arange`
279279

280-
### Status
280+
#### Status
281281

282282
Experimental
283283

284-
### Class
284+
#### Class
285285

286286
Pure function.
287287

288-
### Description
288+
#### Description
289289

290290
Creates a one-dimensional `array` of the `integer/real` type with fixed-spaced values of given spacing, within a given interval.
291291

292-
### Syntax
292+
#### Syntax
293293

294294
`result = [[stdlib_math(module):arange(interface)]](start [, end, step])`
295295

296-
### Arguments
296+
#### Arguments
297297

298298
All arguments should be the same type and kind.
299299

@@ -309,18 +309,18 @@ The default `end` value is the inputted `start` value.
309309
This is an `intent(in)` and `optional` argument.
310310
The default `step` value is `1`.
311311

312-
#### Warning
312+
##### Warning
313313
If `step = 0`, the `step` argument will be corrected to `1/1.0` by the internal process of the `arange` function.
314314
If `step < 0`, the `step` argument will be corrected to `abs(step)` by the internal process of the `arange` function.
315315

316-
### Return value
316+
#### Return value
317317

318318
Returns a one-dimensional `array` of fixed-spaced values.
319319

320320
For `integer` type arguments, the length of the result vector is `(end - start)/step + 1`.
321321
For `real` type arguments, the length of the result vector is `floor((end - start)/step) + 1`.
322322

323-
### Example
323+
#### Example
324324

325325
```fortran
326326
program demo_math_arange
@@ -342,4 +342,68 @@ program demo_math_arange
342342
print *, arange(0.0,2.0,0.0) !! [0.0,1.0,2.0]. Not recommended: `step` argument is zero!
343343
344344
end program demo_math_arange
345+
```
346+
347+
### `is_close`
348+
349+
#### Description
350+
351+
Returns a boolean scalar/array where two scalars/arrays are element-wise equal within a tolerance.
352+
353+
The tolerance values are positive, typically very small numbers. The relative difference `(rtol*abs(b))` and the absolute difference `atol` are added together to compare against the absolute difference between `a` and `b`.
354+
355+
```fortran
356+
!> For `real` type
357+
abs(a - b) <= rtol*abs(b) + atol
358+
359+
!> For `complex` type
360+
abs(a%re - b%re) <= rtol*abs(b%re) + atol
361+
abs(a%im - b%im) <= rtol*abs(b%im) + atol
362+
```
363+
364+
#### Syntax
365+
366+
`bool = [[stdlib_math(module):is_close(interface)]] (a, b [, rtol, atol])`
367+
368+
#### Status
369+
370+
Experimental.
371+
372+
#### Class
373+
374+
Elemental function.
375+
376+
#### Arguments
377+
378+
`a`: Shall be a `real/complex` scalar/array.
379+
This argument is `intent(in)`.
380+
381+
`b`: Shall be a `real/complex` scalar/array.
382+
This argument is `intent(in)`.
383+
384+
`rtol`: Shall be a `real` scalar.
385+
This argument is `intent(in)` and `optional`, which is `1.0e-5` by default.
386+
387+
`atol`: Shall be a `real` scalar.
388+
This argument is `intent(in)` and `optional`, which is `1.0e-8` by default.
389+
390+
Note: All `real/complex` arguments must have same `kind`.
391+
If the value of `rtol/atol` is negative (not recommended), it will be corrected to `abs(rtol/atol)` by the internal process of `is_close`.
392+
393+
#### Result value
394+
395+
Returns a `logical` scalar/array.
396+
397+
#### Example
398+
399+
```fortran
400+
program demo_math_is_close
401+
use stdlib_math, only: is_close
402+
use stdlib_error, only: check
403+
real :: x(2) = [1, 2]
404+
print *, is_close(x,[real :: 1, 2.1]) !! [T, F]
405+
print *, is_close(2.0, 2.1, atol=0.1) !! T
406+
call check(all(is_close(x, [2.0, 2.0])), msg="all(is_close(x, [2.0, 2.0])) failed.", warn=.true.)
407+
!! all(is_close(x, [2.0, 2.0])) failed.
408+
end program demo_math_is_close
345409
```

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set(fppFiles
3333
stdlib_math_linspace.fypp
3434
stdlib_math_logspace.fypp
3535
stdlib_math_arange.fypp
36+
stdlib_math_is_close.fypp
3637
stdlib_string_type.fypp
3738
)
3839

src/Makefile.manual

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ SRCFYPP =\
2929
stdlib_math.fypp \
3030
stdlib_math_linspace.fypp \
3131
stdlib_math_logspace.fypp \
32+
stdlib_math_is_close.fypp \
3233
stdlib_stats_distribution_PRNG.fypp \
3334
stdlib_string_type.fypp
3435

@@ -159,3 +160,5 @@ stdlib_math_logspace.o: \
159160
stdlib_math_arange.o: \
160161
stdlib_math.o
161162
stdlib_linalg_outer_product.o: stdlib_linalg.o
163+
stdlib_math_is_close.o: \
164+
stdlib_math.o

src/stdlib_math.fypp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module stdlib_math
1111
public :: clip, linspace, logspace
1212
public :: EULERS_NUMBER_SP, EULERS_NUMBER_DP, EULERS_NUMBER_QP
1313
public :: DEFAULT_LINSPACE_LENGTH, DEFAULT_LOGSPACE_BASE, DEFAULT_LOGSPACE_LENGTH
14-
public :: arange
14+
public :: arange, is_close
1515

1616
integer, parameter :: DEFAULT_LINSPACE_LENGTH = 100
1717
integer, parameter :: DEFAULT_LOGSPACE_LENGTH = 50
@@ -279,6 +279,21 @@ module stdlib_math
279279
#:endfor
280280
end interface arange
281281

282+
!> Version: experimental
283+
!>
284+
!> Determines whether the values of `a` and `b` are close.
285+
!> ([Specification](../page/specs/stdlib_logic.html#is_close))
286+
interface is_close
287+
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES
288+
#:for k1, t1 in RC_KINDS_TYPES
289+
elemental module function is_close_${t1[0]}$${k1}$(a, b, rtol, atol) result(result)
290+
${t1}$, intent(in) :: a, b
291+
real(${k1}$), intent(in), optional :: rtol, atol
292+
logical :: result
293+
end function is_close_${t1[0]}$${k1}$
294+
#:endfor
295+
end interface is_close
296+
282297
contains
283298

284299
#:for k1, t1 in IR_KINDS_TYPES

src/stdlib_math_is_close.fypp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#:include "common.fypp"
2+
3+
submodule(stdlib_math) stdlib_math_is_close
4+
5+
contains
6+
7+
#! Determines whether the values of `a` and `b` are close.
8+
9+
#:for k1, t1 in REAL_KINDS_TYPES
10+
elemental module function is_close_${t1[0]}$${k1}$(a, b, rtol, atol) result(result)
11+
${t1}$, intent(in) :: a, b
12+
real(${k1}$), intent(in), optional :: rtol, atol
13+
logical :: result
14+
15+
result = abs(a - b) <= abs(optval(rtol, 1.0e-5_${k1}$)*b) + &
16+
abs(optval(atol, 1.0e-8_${k1}$))
17+
18+
end function is_close_${t1[0]}$${k1}$
19+
#:endfor
20+
21+
#:for k1, t1 in CMPLX_KINDS_TYPES
22+
elemental module function is_close_${t1[0]}$${k1}$(a, b, rtol, atol) result(result)
23+
${t1}$, intent(in) :: a, b
24+
real(${k1}$), intent(in), optional :: rtol, atol
25+
logical :: result
26+
27+
result = is_close_r${k1}$(a%re, b%re, rtol, atol) .and. &
28+
is_close_r${k1}$(a%im, b%im, rtol, atol)
29+
30+
end function is_close_${t1[0]}$${k1}$
31+
#:endfor
32+
33+
end submodule stdlib_math_is_close

src/tests/math/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ADDTEST(stdlib_math)
22
ADDTEST(linspace)
33
ADDTEST(logspace)
4-
ADDTEST(math_arange)
4+
ADDTEST(math_arange)
5+
ADDTEST(math_is_close)

src/tests/math/Makefile.manual

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
PROGS_SRC = test_stdlib_math.f90 test_linspace.f90 test_logspace.f90 \
2-
test_math_arange.f90
2+
test_math_arange.f90 \
3+
test_math_is_close.f90
34

45

56
include ../Makefile.manual.test.mk

src/tests/math/test_math_is_close.f90

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
program test_math_is_close
2+
3+
call test_math_is_close_real
4+
call test_math_is_close_complex
5+
print *, "All tests in `test_math_is_close` passed."
6+
7+
contains
8+
9+
subroutine test_math_is_close_real
10+
use stdlib_math, only: is_close
11+
use stdlib_error, only: check
12+
13+
call check(is_close(2.5, 2.5, rtol=1.0e-5), msg="is_close(2.5, 2.5, rtol=1.0e-5) failed.")
14+
call check(all(is_close([2.5, 3.2], [2.5, 10.0], rtol=1.0e-5)), &
15+
msg="all(is_close([2.5, 3.2], [2.5, 10.0], rtol=1.0e-5)) failed (expected).", warn=.true.)
16+
call check(all(is_close(reshape([2.5, 3.2, 2.2, 1.0], [2, 2]), reshape([2.5, 3.2001, 2.25, 1.1], [2, 2]), &
17+
atol=1.0e-5, rtol=0.1)), &
18+
msg="all(is_close(reshape([2.5, 3.2, 2.2, 1.0],[2,2]), reshape([2.5, 3.2001, 2.25, 1.1],[2,2]), &
19+
&rtol=1.0e-5, atol=0.1)) failed.")
20+
21+
end subroutine test_math_is_close_real
22+
23+
subroutine test_math_is_close_complex
24+
use stdlib_math, only: is_close
25+
use stdlib_error, only: check
26+
27+
call check(is_close((2.5,1.2), (2.5,1.2), rtol=1.0e-5), &
28+
msg="is_close((2.5,1.2), (2.5,1.2), rtol=1.0e-5) failed.")
29+
call check(all(is_close([(2.5,1.2), (3.2,1.2)], [(2.5,1.2), (10.0,1.2)], rtol=1.0e-5)), &
30+
msg="all(is_close([(2.5,1.2), (3.2,1.2)], [(2.5,1.2), (10.0,1.2)], rtol=1.0e-5)) failed (expected).", &
31+
warn=.true.)
32+
call check(all(is_close(reshape([(2.5,1.2009), (3.2,1.199999)], [1, 2]), reshape([(2.4,1.2009), (3.15,1.199999)], [1, 2]), &
33+
atol=1.0e-5, rtol=0.1)), &
34+
msg="all(is_close(reshape([(2.5,1.2009), (3.2,1.199999)], [1, 2]), &
35+
&reshape([(2.4,1.2009), (3.15,1.199999)], [1, 2]), &
36+
&rtol=1.0e-5, atol=0.1)) failed.")
37+
38+
end subroutine test_math_is_close_complex
39+
40+
end program test_math_is_close

0 commit comments

Comments
 (0)