|
93 | 93 | @test meetstol(2.1f0^-71, 1f-6)
|
94 | 94 | @test meetstol(2.0f0^60, 1e-15)
|
95 | 95 | end
|
| 96 | + |
| 97 | +#### A few critical values, but first, a few useful functions |
| 98 | +ixword(x::Float64) = (reinterpret(UInt64, x) >>> 32 & Int32) & 0x7fffffff |
| 99 | +ixword(x::Float32) = reinterpret(Int32, x) & 0x7fffffff |
| 100 | + |
| 101 | +function ulp(x::Float32) |
| 102 | + z′ = logabsgamma(x)[1] |
| 103 | + z = logabsgamma(Float64(x))[1] # Test against our Float64 implementation |
| 104 | + isinf(z′) && isinf(oftype(x, z)) && return 0.0 |
| 105 | + iszero(z′) && iszero(z) && return 0.0 |
| 106 | + e = exponent(z′) |
| 107 | + abs(z′ - z) * 2.0^(precision(x) - 1 - e) |
| 108 | +end |
| 109 | +function ulp(x) |
| 110 | + z′ = logabsgamma(x)[1] |
| 111 | + z = logabsgamma(big(x))[1] # Dispatches to MPFR |
| 112 | + isinf(z′) && isinf(oftype(x, z)) && return big(0.0) |
| 113 | + iszero(z′) && iszero(z) && return big(0.0) |
| 114 | + e = exponent(z′) |
| 115 | + abs(z′ - z) * 2.0^(precision(x) - 1 - e) |
| 116 | +end |
| 117 | + |
| 118 | + |
| 119 | +# interval: 0 < x < 2 |
| 120 | +# First f64 which would not satisfy ixword(x) ≤ 0x40000000: |
| 121 | +x = 2.000001907348633 |
| 122 | +# That is, all values between 2.0 and 2.000001907348633 would inappropriately fall |
| 123 | +# into the trap of ≤ 0x40000000, thereby producing completely incorrect values. |
| 124 | +# Hence, we must carefully test to ensure that this small region is computed |
| 125 | +# appropriately. |
| 126 | + |
| 127 | +@test ulp(x) < 0.1511442187367193 |
| 128 | +@test ulp(2.0) == 0.0 |
| 129 | +r = 2.0:1e-9:prevfloat(x) |
| 130 | +@test all(x -> ulp(x) < 1.0, r) |
| 131 | + |
| 132 | +# first f32 greater than 2.0f0: |
| 133 | +x = 2.0000002f0 |
| 134 | +@test ulp(x) < 0.197537131607533 |
| 135 | +# unlike f64, the representable distance between values is too small to matter |
| 136 | +# -- i.e. prevfloat(2.0000002f0) == 2.0f0; we check behavior anyway |
| 137 | +@test ulp(2.0f0) == 0.0 |
| 138 | + |
| 139 | + |
| 140 | +# interval: 2 < x < 8 |
| 141 | +# In fact, the first f64 which does not fall into the x < 8.0 branch is: |
| 142 | +x = 8.000007629394531 |
| 143 | +@test ulp(prevfloat(x)) < 0.625524448102362 |
| 144 | +@test ulp(x) < 0.390118045607547 |
| 145 | +# This is overkill, but should reveal any toying around with this |
| 146 | +# sensitive region. |
| 147 | +r = 8.0:1e-9:prevfloat(x) |
| 148 | +@test all(x -> ulp(x) < 1.5, r) |
| 149 | + |
| 150 | +# first f32 which does not fall into x < 8.0 branch is: |
| 151 | +x = 8.000001f0 |
| 152 | +@test ulp(x) < 0.614982068538667 |
| 153 | +# But, unlike f64, the representable distance between values is too small. |
| 154 | +# (i.e. prevfloat(8.000001f0) == 8.0f0) |
| 155 | +# We still check appropriate behavior at 8.0f0 |
| 156 | +@test ulp(8.0f0) < 0.4006594736129046 |
0 commit comments