Skip to content

Commit 35ca0a0

Browse files
authored
Fix overflows in quantile (#145)
The `a + γ*(b-a)` introduced by JuliaLang/julia#16572 has the advantage that it increases with `γ` even when `a` and `b` are very close, but it has the drawback that it is not robust to overflow. This is likely to happen in practice with small integer and floating point types. Conversely, the `(1-γ)*a + γ*b` which is currently used only for non-finite quantities is robust to overflow but may not always increase with `γ` as when `a` and `b` are very close or (more frequently) equal since precision loss can give a slightly smaller value for a larger `γ`. This can be problematic as it breaks an expected invariant. So keep using the `a + γ*(b-a)` formula when `a ≈ b`, in which case it's almost like returning either `a` or `b` but less arbitrary.
1 parent bb7063d commit 35ca0a0

File tree

2 files changed

+21
-1
lines changed

2 files changed

+21
-1
lines changed

src/Statistics.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,10 @@ end
10201020
b = v[j + 1]
10211021
end
10221022

1023-
if isfinite(a) && isfinite(b)
1023+
# When a ≉ b, b-a may overflow
1024+
# When a ≈ b, (1-γ)*a + γ*b may not be increasing with γ due to rounding
1025+
# Call to float is to work around JuliaLang/julia#50380
1026+
if isfinite(a) && isfinite(b) && float(a) float(b)
10241027
return a + γ*(b-a)
10251028
else
10261029
return (1-γ)*a + γ*b

test/runtests.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,23 @@ let y = [0.40003674665581906, 0.4085630862624367, 0.41662034698690303, 0.4166203
753753
@test issorted(quantile(y, range(0.01, stop=0.99, length=17)))
754754
end
755755

756+
@testset "issue #144: no overflow with quantile" begin
757+
@test quantile(Float16[-9000, 100], 1.0) 100
758+
@test quantile(Float16[-9000, 100], 0.999999999) 99.99999
759+
@test quantile(Float32[-1e9, 100], 1) 100
760+
@test quantile(Float32[-1e9, 100], 0.9999999999) 99.89999998
761+
@test quantile(Float64[-1e20, 100], 1) 100
762+
@test quantile(Float32[-1e15, -1e14, -1e13, -1e12, -1e11, -1e10, -1e9, 100], 1) 100
763+
@test quantile(Int8[-68, 60], 0.5) -4
764+
@test quantile(Int32[-1e9, 2e9], 1.0) 2.0e9
765+
@test quantile(Int64[-5e18, -2e18, 9e18], 1.0) 9.0e18
766+
767+
# check that quantiles are increasing with a, b and p even in corner cases
768+
@test issorted(quantile([1.0, 1.0, 1.0+eps(), 1.0+eps()], range(0, 1, length=100)))
769+
@test issorted(quantile([1.0, 1.0+1eps(), 1.0+2eps(), 1.0+3eps()], range(0, 1, length=100)))
770+
@test issorted(quantile([1.0, 1.0+2eps(), 1.0+4eps(), 1.0+6eps()], range(0, 1, length=100)))
771+
end
772+
756773
@testset "variance of complex arrays (#13309)" begin
757774
z = rand(ComplexF64, 10)
758775
@test var(z) invoke(var, Tuple{Any}, z) cov(z) var(z,dims=1)[1] sum(abs2, z .- mean(z))/9

0 commit comments

Comments
 (0)