Skip to content

Commit c50c579

Browse files
nalimilanIanButterworth
authored andcommitted
Fix integer overflow in isapprox (#50730)
Ensure that `isapprox` gives correct results when comparing an integer with another integer or with a float. For comparison between integers, the fix only works when keeping default values for `rtol` and `norm`, and with `atol < 1`. It is not possible to handle the (atypical) case where `norm !== abs`, but that's OK since the user is responsible for providing a safe function. It would be possible to handle the case where `rtol > 0` or `atol >= 1`, but with complex code which would check for overflow and handle all possible corner cases; it would work only for types defined in Base and would not be extensible by packages. So I'm not sure that's worth it. At least with PR fixes the most common case. Fixes #50380. (cherry picked from commit 5f03a18)
1 parent cf29443 commit c50c579

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

base/floatfuncs.jl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,20 @@ true
304304
function isapprox(x::Number, y::Number;
305305
atol::Real=0, rtol::Real=rtoldefault(x,y,atol),
306306
nans::Bool=false, norm::Function=abs)
307-
x == y || (isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))) || (nans && isnan(x) && isnan(y))
307+
x′, y′ = promote(x, y) # to avoid integer overflow
308+
x == y ||
309+
(isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(x′), norm(y′)))) ||
310+
(nans && isnan(x) && isnan(y))
311+
end
312+
313+
function isapprox(x::Integer, y::Integer;
314+
atol::Real=0, rtol::Real=rtoldefault(x,y,atol),
315+
nans::Bool=false, norm::Function=abs)
316+
if norm === abs && atol < 1 && rtol == 0
317+
return x == y
318+
else
319+
return norm(x - y) <= max(atol, rtol*max(norm(x), norm(y)))
320+
end
308321
end
309322

310323
"""

test/floatfuncs.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,47 @@ end
209209
struct CustomNumber <: Number end
210210
@test !isnan(CustomNumber())
211211
end
212+
213+
@testset "isapprox and integer overflow" begin
214+
for T in (Int8, Int16, Int32)
215+
T === Int && continue
216+
@test !isapprox(typemin(T), T(0))
217+
@test !isapprox(typemin(T), unsigned(T)(0))
218+
@test !isapprox(typemin(T), 0)
219+
@test !isapprox(typemin(T), T(0), atol=0.99)
220+
@test !isapprox(typemin(T), unsigned(T)(0), atol=0.99)
221+
@test !isapprox(typemin(T), 0, atol=0.99)
222+
@test_broken !isapprox(typemin(T), T(0), atol=1)
223+
@test_broken !isapprox(typemin(T), unsigned(T)(0), atol=1)
224+
@test !isapprox(typemin(T), 0, atol=1)
225+
226+
@test !isapprox(typemin(T)+T(10), T(10))
227+
@test !isapprox(typemin(T)+T(10), unsigned(T)(10))
228+
@test !isapprox(typemin(T)+T(10), 10)
229+
@test !isapprox(typemin(T)+T(10), T(10), atol=0.99)
230+
@test !isapprox(typemin(T)+T(10), unsigned(T)(10), atol=0.99)
231+
@test !isapprox(typemin(T)+T(10), 10, atol=0.99)
232+
@test_broken !isapprox(typemin(T)+T(10), T(10), atol=1)
233+
@test !isapprox(typemin(T)+T(10), unsigned(T)(10), atol=1)
234+
@test !isapprox(typemin(T)+T(10), 10, atol=1)
235+
236+
@test isapprox(typemin(T), 0.0, rtol=1)
237+
end
238+
for T in (Int, Int64, Int128)
239+
@test !isapprox(typemin(T), T(0))
240+
@test !isapprox(typemin(T), unsigned(T)(0))
241+
@test !isapprox(typemin(T), T(0), atol=0.99)
242+
@test !isapprox(typemin(T), unsigned(T)(0), atol=0.99)
243+
@test_broken !isapprox(typemin(T), T(0), atol=1)
244+
@test_broken !isapprox(typemin(T), unsigned(T)(0), atol=1)
245+
246+
@test !isapprox(typemin(T)+T(10), T(10))
247+
@test !isapprox(typemin(T)+T(10), unsigned(T)(10))
248+
@test !isapprox(typemin(T)+T(10), T(10), atol=0.99)
249+
@test !isapprox(typemin(T)+T(10), unsigned(T)(10), atol=0.99)
250+
@test_broken !isapprox(typemin(T)+T(10), T(10), atol=1)
251+
@test !isapprox(typemin(T)+T(10), unsigned(T)(10), atol=1)
252+
253+
@test isapprox(typemin(T), 0.0, rtol=1)
254+
end
255+
end

0 commit comments

Comments
 (0)