diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 970cd8cb..7d0092bc 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -200,9 +200,19 @@ saturating_sub(x::X, y::X) where {X <: FixedPoint} = saturating_sub(x::X, y::X) where {X <: FixedPoint{<:Unsigned}} = X(x.i - min(x.i, y.i), 0) # checked arithmetic -checked_neg(x::X) where {X <: FixedPoint} = X(checked_neg(x.i), 0) -checked_add(x::X, y::X) where {X <: FixedPoint} = X(checked_add(x.i, y.i), 0) -checked_sub(x::X, y::X) where {X <: FixedPoint} = X(checked_sub(x.i, y.i), 0) +checked_neg(x::X) where {X <: FixedPoint} = checked_sub(zero(X), x) +function checked_add(x::X, y::X) where {X <: FixedPoint} + r, f = Base.Checked.add_with_overflow(x.i, y.i) + z = X(r, 0) # store first + f && throw_overflowerror(:+, x, y) + z +end +function checked_sub(x::X, y::X) where {X <: FixedPoint} + r, f = Base.Checked.sub_with_overflow(x.i, y.i) + z = X(r, 0) # store first + f && throw_overflowerror(:-, x, y) + z +end # default arithmetic const DEFAULT_ARITHMETIC = :wrapping @@ -424,6 +434,13 @@ scaledual(::Type{Tdual}, x::AbstractArray{T}) where {Tdual, T <: FixedPoint} = throw(ArgumentError(String(take!(io)))) end +@noinline function throw_overflowerror(op::Symbol, @nospecialize(x), @nospecialize(y)) + io = IOBuffer() + print(io, x, ' ', op, ' ', y, " overflowed for type ") + showtype(io, typeof(x)) + throw(OverflowError(String(take!(io)))) +end + function Random.rand(r::AbstractRNG, ::SamplerType{X}) where X <: FixedPoint X(rand(r, rawtype(X)), 0) end diff --git a/test/fixed.jl b/test/fixed.jl index 251934b8..205fb9a0 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -275,8 +275,8 @@ end xs = typemin(F):eps(F):typemax(F) fneg(x) = -float(x) @test all(x -> wrapping_neg(wrapping_neg(x)) === x, xs) - @test all(x -> saturating_neg(x) == clamp(fneg(x), F), xs) - @test all(x -> !(typemin(F) < fneg(x) < typemax(F)) || + @test all(x -> saturating_neg(x) === clamp(fneg(x), F), xs) + @test all(x -> !(typemin(F) <= fneg(x) <= typemax(F)) || wrapping_neg(x) === checked_neg(x) === fneg(x) % F, xs) end end @@ -301,8 +301,8 @@ end xys = ((x, y) for x in xs, y in xs) fadd(x, y) = float(x) + float(y) @test all(((x, y),) -> wrapping_sub(wrapping_add(x, y), y) === x, xys) - @test all(((x, y),) -> saturating_add(x, y) == clamp(fadd(x, y), F), xys) - @test all(((x, y),) -> !(typemin(F) < fadd(x, y) < typemax(F)) || + @test all(((x, y),) -> saturating_add(x, y) === clamp(fadd(x, y), F), xys) + @test all(((x, y),) -> !(typemin(F) <= fadd(x, y) <= typemax(F)) || wrapping_add(x, y) === checked_add(x, y) === fadd(x, y) % F, xys) end end diff --git a/test/normed.jl b/test/normed.jl index 6c751724..c2e1c6e3 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -303,7 +303,7 @@ end fneg(x) = -float(x) @test all(x -> wrapping_neg(wrapping_neg(x)) === x, xs) @test all(x -> saturating_neg(x) === clamp(fneg(x), N), xs) - @test all(x -> !(typemin(N) < fneg(x) < typemax(N)) || + @test all(x -> !(typemin(N) <= fneg(x) <= typemax(N)) || wrapping_neg(x) === checked_neg(x) === fneg(x) % N, xs) end end @@ -329,7 +329,7 @@ end fadd(x, y) = float(x) + float(y) @test all(((x, y),) -> wrapping_sub(wrapping_add(x, y), y) === x, xys) @test all(((x, y),) -> saturating_add(x, y) === clamp(fadd(x, y), N), xys) - @test all(((x, y),) -> !(typemin(N) < fadd(x, y) < typemax(N)) || + @test all(((x, y),) -> !(typemin(N) <= fadd(x, y) <= typemax(N)) || wrapping_add(x, y) === checked_add(x, y) === fadd(x, y) % N, xys) end end @@ -354,7 +354,7 @@ end fsub(x, y) = float(x) - float(y) @test all(((x, y),) -> wrapping_add(wrapping_sub(x, y), y) === x, xys) @test all(((x, y),) -> saturating_sub(x, y) === clamp(fsub(x, y), N), xys) - @test all(((x, y),) -> !(typemin(N) < fsub(x, y) < typemax(N)) || + @test all(((x, y),) -> !(typemin(N) <= fsub(x, y) <= typemax(N)) || wrapping_sub(x, y) === checked_sub(x, y) === fsub(x, y) % N, xys) end end