|
1 | 1 | module ULPError |
2 | | - export ulp_error, ulp_error_maximum |
3 | | - function ulp_error(accurate::AbstractFloat, approximate::AbstractFloat) |
4 | | - # the ULP error is usually not required to great accuracy, so `Float32` should be precise enough |
5 | | - zero_return = 0f0 |
6 | | - inf_return = Inf32 |
7 | | - # handle floating-point edge cases |
8 | | - if !(isfinite(accurate) && isfinite(approximate)) |
9 | | - accur_is_nan = isnan(accurate) |
10 | | - approx_is_nan = isnan(approximate) |
11 | | - if accur_is_nan || approx_is_nan |
12 | | - return if accur_is_nan === approx_is_nan |
13 | | - zero_return |
14 | | - else |
15 | | - inf_return |
16 | | - end |
17 | | - end |
18 | | - if isinf(approximate) |
19 | | - return if isinf(accurate) && (signbit(accurate) == signbit(approximate)) |
20 | | - zero_return |
21 | | - else |
22 | | - inf_return |
23 | | - end |
| 2 | + |
| 3 | +export ulp_error, ulp_error_maximum |
| 4 | + |
| 5 | +function ulp_error(accurate::AbstractFloat, approximate::AbstractFloat) |
| 6 | + # the ULP error is usually not required to great accuracy, so `Float32` should be precise enough |
| 7 | + zero_return = 0f0 |
| 8 | + inf_return = Inf32 |
| 9 | + # handle floating-point edge cases |
| 10 | + if !(isfinite(accurate) && isfinite(approximate)) |
| 11 | + accur_is_nan = isnan(accurate) |
| 12 | + approx_is_nan = isnan(approximate) |
| 13 | + if accur_is_nan || approx_is_nan |
| 14 | + return if accur_is_nan === approx_is_nan |
| 15 | + zero_return |
| 16 | + else |
| 17 | + inf_return |
24 | 18 | end |
25 | 19 | end |
26 | | - acc = if accurate isa Union{Float16, Float32} |
27 | | - # widen for better accuracy when doing so does not impact performance too much |
28 | | - widen(accurate) |
29 | | - else |
30 | | - accurate |
| 20 | + if isinf(approximate) |
| 21 | + return if isinf(accurate) && (signbit(accurate) == signbit(approximate)) |
| 22 | + zero_return |
| 23 | + else |
| 24 | + inf_return |
| 25 | + end |
31 | 26 | end |
32 | | - abs(Float32((approximate - acc) / eps(approximate))::Float32) |
33 | | - end |
34 | | - function ulp_error(accurate::Acc, approximate::App, x::AbstractFloat) where {Acc, App} |
35 | | - acc = accurate(x) |
36 | | - app = approximate(x) |
37 | | - ulp_error(acc, app) |
38 | | - end |
39 | | - function ulp_error(func::Func, x::AbstractFloat) where {Func} |
40 | | - ulp_error(func ∘ BigFloat, func, x) |
41 | 27 | end |
42 | | - function ulp_error_maximum(func::Func, iterator) where {Func} |
43 | | - maximum(Base.Fix1(ulp_error, func), iterator) |
| 28 | + acc = if accurate isa Union{Float16, Float32} |
| 29 | + # widen for better accuracy when doing so does not impact performance too much |
| 30 | + widen(accurate) |
| 31 | + else |
| 32 | + accurate |
44 | 33 | end |
| 34 | + abs(Float32((approximate - acc) / eps(approximate))::Float32) |
| 35 | +end |
| 36 | + |
| 37 | +function ulp_error(accurate::Acc, approximate::App, x::AbstractFloat) where {Acc, App} |
| 38 | + acc = accurate(x) |
| 39 | + app = approximate(x) |
| 40 | + ulp_error(acc, app) |
| 41 | +end |
| 42 | + |
| 43 | +function ulp_error(func::Func, x::AbstractFloat) where {Func} |
| 44 | + ulp_error(func ∘ BigFloat, func, x) |
| 45 | +end |
| 46 | + |
| 47 | +function ulp_error_maximum(func::Func, iterator) where {Func} |
| 48 | + maximum(Base.Fix1(ulp_error, func), iterator) |
| 49 | +end |
| 50 | + |
45 | 51 | end |
0 commit comments