Skip to content

JuliaMath/CheckedArithmetic.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CheckedArithmetic

This package aims to make it easier to detect overflow in numeric computations. It exports two macros, @check and @checked, as well as functions accumulatortype and acc. Packages can add support for their own types to interact appropriately with these tools.

@checked

@checked converts arithmetic operators to their checked variants. For example,

@checked z = x + y

rewrites this expression as

z = Base.Checked.checked_add(x, y)

Note that this macro only operates at the level of surface syntax, i.e.,

@checked z = f(x) + f(y)

will not detect overflow caused by f.

The Base.Checked module defines numerous checked operations. These can be specialized for custom types.

@check

@check performs an operation in two different ways, checking that both approaches agree. @check is primarily useful in tests.

For example,

d = @check ssd(a, b)

would perform ssd(a, b) with the inputs as given, and also compute ssd(asafe, bsafe) where asafe and bsafe are "safer" variants of a and b. It then tests whether the result obtained from the "safe" arguments is consistent with the result obtained from a and b. If the two differ to within the precision of the "ordinary" (unsafe) result, an error is thrown. Optionally, you can supply keywords accepted by isapprox:

@check foo(a) atol=1e-12

Packages can specialize CheckedArithmetic.safearg to control how asafe and bsafe are generated. To guard against oversights, safearg must be explicitly defined for each numeric type---the fallback method for safearg(::Number) is to throw an error.

accumulatortype and acc

These functions are useful for writing overflow-safe algorithms. accumulatortype(T) or accumulatortype(T1, T2...) takes types as input arguments and returns a type suitable for limited-risk arithmetic operations. acc(x) is just shorthand for convert(accumulatortype(typeof(x)), x).

You can also specialize on the operation. For example,

julia> accumulatortype(+, UInt8)
UInt64

julia> accumulatortype(-, UInt8)
Int64

If you're computing a sum-of-squares and want to make sure you algorithm is (reasonably) safe for an input array of UInt8 numbers, you might want to write that as

function sumsquares(A::AbstractArray)
    s = zero(accumulatortype(eltype(A)))
    for a in A
        s += acc(a)^2
    end
    return s
end