Skip to content

Commit 28b3b6a

Browse files
authored
Add div family methods (div, cld, fld, rem, mod) (#16)
Plus a few other fixups
1 parent 12afe1c commit 28b3b6a

File tree

6 files changed

+466
-39
lines changed

6 files changed

+466
-39
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "OverflowContexts"
22
uuid = "649716ba-0eb1-4560-ace2-251185f55281"
33
authors = ["Nicholas Bauer <[email protected]>"]
4-
version = "0.2.7"
4+
version = "0.3"
55

66
[compat]
77
julia = "1"

src/OverflowContexts.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ include("base_ext_sat.jl")
66
include("abstractarraymath_ext.jl")
77

88
export @default_checked, @default_unchecked, @default_saturating, @checked, @unchecked, @saturating,
9-
checked_neg, checked_add, checked_sub, checked_mul, checked_pow, checked_negsub, checked_abs,
10-
unchecked_neg, unchecked_add, unchecked_sub, unchecked_mul, unchecked_negsub, unchecked_pow, unchecked_abs,
11-
saturating_neg, saturating_add, saturating_sub, saturating_mul, saturating_pow, saturating_negsub, saturating_abs
9+
checked_neg, checked_add, checked_sub, checked_mul, checked_pow, checked_negsub, checked_abs, checked_div, checked_fld, checked_cld, checked_rem, checked_mod,
10+
unchecked_neg, unchecked_add, unchecked_sub, unchecked_mul, unchecked_negsub, unchecked_pow, unchecked_abs, unchecked_div, unchecked_fld, unchecked_cld, unchecked_rem, unchecked_mod,
11+
saturating_neg, saturating_add, saturating_sub, saturating_mul, saturating_pow, saturating_negsub, saturating_abs, saturating_div, saturating_fld, saturating_cld, saturating_rem, saturating_mod
1212

1313
end # module

src/base_ext.jl

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Base: BitInteger, promote, afoldl, @_inline_meta
2-
import Base.Checked: checked_neg, checked_add, checked_sub, checked_mul, checked_abs
2+
import Base.Checked: checked_neg, checked_add, checked_sub, checked_mul, checked_abs,
3+
checked_div, checked_fld, checked_cld, checked_mod, checked_rem
34
using Base.Checked: mul_with_overflow
45

56
if VERSION v"1.11-alpha"
@@ -10,6 +11,9 @@ else
1011
using Base.Checked: throw_overflowerr_binaryop
1112
end
1213

14+
const SignedBitInteger = Union{Int8, Int16, Int32, Int64, Int128}
15+
const UnsignedBitInteger = Union{UInt8, UInt16, UInt32, UInt64, UInt128}
16+
1317
# The Base methods have unchecked semantics, so just pass through
1418
unchecked_neg(x...) = Base.:-(x...)
1519
unchecked_add(x...) = Base.:+(x...)
@@ -18,6 +22,13 @@ unchecked_mul(x...) = Base.:*(x...)
1822
unchecked_pow(x...) = Base.:^(x...)
1923
unchecked_abs(x...) = Base.abs(x...)
2024

25+
# The Base div methods have checked semantics, so just pass through
26+
checked_div(x...) = Base.:÷(x...)
27+
checked_fld(x...) = Base.fld(x...)
28+
checked_cld(x...) = Base.cld(x...)
29+
checked_rem(x...) = Base.:%(x...) # Yes, % is `rem`, not `mod`
30+
checked_mod(x...) = Base.mod(x...)
31+
checked_divrem(x...) = Base.divrem(x...)
2132

2233
# convert multi-argument calls into nested two-argument calls
2334
checked_add(a, b, c, xs...) = @checked (@_inline_meta; afoldl(+, (+)((+)(a, b), c), xs...))
@@ -40,6 +51,13 @@ saturating_sub(x::Number, y::Number) = saturating_sub(promote(x, y)...)
4051
saturating_mul(x::Number, y::Number) = saturating_mul(promote(x, y)...)
4152
saturating_pow(x::Number, y::Number) = saturating_pow(promote(x, y)...)
4253

54+
saturating_div(x::Number, y::Number) = saturating_div(promote(x, y)...)
55+
saturating_fld(x::Number, y::Number) = saturating_fld(promote(x, y)...)
56+
saturating_cld(x::Number, y::Number) = saturating_cld(promote(x, y)...)
57+
saturating_rem(x::Number, y::Number) = saturating_rem(promote(x, y)...)
58+
saturating_mod(x::Number, y::Number) = saturating_mod(promote(x, y)...)
59+
saturating_divrem(x::Number, y::Number) = saturating_divrem(promote(x, y)...)
60+
4361

4462
# fallback to `unchecked_` for `Number` types that don't have more specific `checked_` methods
4563
checked_neg(x::T) where T <: Number = unchecked_neg(x)
@@ -56,6 +74,13 @@ saturating_mul(x::T, y::T) where T <: Number = unchecked_mul(x, y)
5674
saturating_pow(x::T, y::T) where T <: Number = unchecked_pow(x, y)
5775
saturating_abs(x::T) where T <: Number = unchecked_abs(x)
5876

77+
saturating_div(x::T, y::T) where T <: Number = saturating_div(x, y)
78+
saturating_fld(x::T, y::T) where T <: Number = saturating_fld(x, y)
79+
saturating_cld(x::T, y::T) where T <: Number = saturating_cld(x, y)
80+
saturating_rem(x::T, y::T) where T <: Number = saturating_rem(x, y)
81+
saturating_mod(x::T, y::T) where T <: Number = saturating_mod(x, y)
82+
saturating_divrem(x::T, y::T) where T <: Number = saturating_divrem(x, y)
83+
5984

6085
# fallback to `unchecked_` for non-`Number` types
6186
checked_neg(x) = unchecked_neg(x)
@@ -66,6 +91,66 @@ checked_pow(x, y) = unchecked_pow(x, y)
6691
checked_abs(x) = unchecked_abs(x)
6792

6893

94+
# fallback to `checked_` div methods for non-`Number` types
95+
unchecked_div(x, y) = checked_div(x, y)
96+
unchecked_fld(x, y) = checked_fld(x, y)
97+
unchecked_cld(x, y) = checked_cld(x, y)
98+
unchecked_rem(x, y) = checked_rem(x, y)
99+
unchecked_mod(x, y) = checked_mod(x, y)
100+
unchecked_divrem(x, y) = checked_divrem(x, y)
101+
102+
103+
# unchecked div implementations
104+
# integer division is so slow that these branches don't seem to matter?
105+
# We're making integer division unchecked by letting `÷ -1` just be negated unchecked,
106+
# and `÷ 0` be 0.
107+
unchecked_div(x::T, y::T) where T <: SignedBitInteger =
108+
(y == zero(T)) ?
109+
zero(T) :
110+
(y == -one(T)) ?
111+
-x :
112+
Base.sdiv_int(x, y)
113+
unchecked_div(x::T, y::T) where T <: UnsignedBitInteger =
114+
(y == zero(T)) ?
115+
zero(T) :
116+
Base.udiv_int(x, y)
117+
118+
function unchecked_fld(x::T, y::T) where T <: SignedBitInteger
119+
d = unchecked_div(x, y)
120+
return y == 0 ?
121+
d :
122+
d - (signbit(x y) & (d * y != x))
123+
end
124+
unchecked_fld(x::T, y::T) where T <: UnsignedBitInteger = unchecked_div(x, y)
125+
126+
function unchecked_cld(x::T, y::T) where T <: SignedBitInteger
127+
d = unchecked_div(x, y)
128+
return x == typemin(T) && y == -1 || y == 0 ?
129+
d :
130+
d + (((x > 0) == (y > 0)) & (d * y != x))
131+
end
132+
function unchecked_cld(x::T, y::T) where T <: UnsignedBitInteger
133+
d = unchecked_div(x, y)
134+
return y == 0 ?
135+
d :
136+
d + (d * y != x)
137+
end
138+
139+
unchecked_rem(x::T, y::T) where T <: SignedBitInteger =
140+
(y == zero(T)) ?
141+
x :
142+
(y == -one(T)) ?
143+
zero(T) :
144+
Base.srem_int(x, y)
145+
unchecked_rem(x::T, y::T) where T <: UnsignedBitInteger =
146+
(y == zero(T)) ?
147+
x :
148+
Base.urem_int(x, y)
149+
150+
unchecked_mod(x::T, y::T) where T <: SignedBitInteger = x - unchecked_fld(x, y) * y
151+
unchecked_mod(x::T, y::T) where T <: UnsignedBitInteger = unchecked_rem(x, y)
152+
153+
69154
if VERSION < v"1.11"
70155
# Base.Checked only gained checked powers in 1.11
71156

src/base_ext_sat.jl

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ if VERSION ≤ v"1.11-alpha"
66
end
77

88
# saturating implementations
9-
const SignedBitInteger = Union{Int8, Int16, Int32, Int64, Int128}
10-
119
saturating_neg(x::T) where T <: BitInteger = saturating_sub(zero(T), x)
1210

1311
if VERSION v"1.5"
@@ -68,3 +66,57 @@ function saturating_abs(x::T) where T <: SignedBitInteger
6866
result = flipsign(x, x)
6967
return result < 0 ? typemax(T) : result
7068
end
69+
70+
# for saturating, letting `÷ -1` be negated with saturation, and `÷ 0` be the type min,
71+
# 0, or max based on the sign of the dividend
72+
function saturating_div(x::T, y::T) where T <: SignedBitInteger
73+
return (y == zero(T)) ?
74+
((x == zero(T)) ?
75+
zero(T) :
76+
saturating_mul(-sign(x), typemin(T))) :
77+
(y == -one(T)) ?
78+
saturating_neg(x) :
79+
Base.sdiv_int(x, y)
80+
end
81+
saturating_div(x::T, y::T) where T <: UnsignedBitInteger =
82+
(y == zero(T)) ?
83+
(x == zero(T) ?
84+
zero(T) :
85+
typemax(T)) :
86+
Base.udiv_int(x, y)
87+
88+
function saturating_fld(x::T, y::T) where T <: SignedBitInteger
89+
d = saturating_div(x, y)
90+
return @saturating d - (signbit(x y) & (d * y != x))
91+
end
92+
saturating_fld(x::T, y::T) where T <: UnsignedBitInteger = saturating_div(x, y)
93+
94+
function saturating_cld(x::T, y::T) where T <: SignedBitInteger
95+
d = saturating_div(x, y)
96+
return x == typemin(T) && y == -1 || y == 0 ?
97+
d :
98+
d + (((x > 0) == (y > 0)) & (d * y != x))
99+
end
100+
function saturating_cld(x::T, y::T) where T <: UnsignedBitInteger
101+
d = saturating_div(x, y)
102+
return y == 0 ?
103+
d :
104+
d + (d * y != x)
105+
end
106+
107+
saturating_rem(x::T, y::T) where T <: SignedBitInteger =
108+
(y == zero(T) || y == -one(T)) ?
109+
zero(T) :
110+
Base.srem_int(x, y)
111+
saturating_rem(x::T, y::T) where T <: UnsignedBitInteger =
112+
(y == zero(T)) ?
113+
zero(T) :
114+
Base.urem_int(x, y)
115+
116+
function saturating_mod(x::T, y::T) where T <: SignedBitInteger
117+
return x == typemin(T) && y == -1 || y == 0 ?
118+
(@saturating rem(x, y)) :
119+
@saturating x - fld(x, y) * y
120+
end
121+
122+
saturating_mod(x::T, y::T) where T <: UnsignedBitInteger = @saturating rem(x, y)

src/macros.jl

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Base.Meta: isexpr
22

3-
const op_method_symbols = (:+, :-, :*, :^, :abs)
3+
const op_method_symbols = (:+, :-, :*, :^, :abs, :÷, :div, :cld, :fld, :%, :rem, :mod)
44

55
"""
66
@default_checked
@@ -21,6 +21,13 @@ macro default_checked()
2121
(@__MODULE__).eval(:(*(x...) = checked_mul(x...)))
2222
(@__MODULE__).eval(:(^(x...) = checked_pow(x...)))
2323
(@__MODULE__).eval(:(abs(x) = checked_abs(x)))
24+
(@__MODULE__).eval(:(÷(x...) = checked_div(x...)))
25+
(@__MODULE__).eval(:(div(x) = checked_div(x)))
26+
(@__MODULE__).eval(:(fld(x) = checked_fld(x)))
27+
(@__MODULE__).eval(:(cld(x) = checked_cld(x)))
28+
(@__MODULE__).eval(:(%(x...) = checked_mod(x...)))
29+
(@__MODULE__).eval(:(rem(x) = checked_rem(x)))
30+
(@__MODULE__).eval(:(mod(x) = checked_mod(x)))
2431
(@__MODULE__).eval(:(__OverflowContextDefaultSet = true))
2532
nothing
2633
end
@@ -45,6 +52,13 @@ macro default_unchecked()
4552
(@__MODULE__).eval(:(*(x...) = unchecked_mul(x...)))
4653
(@__MODULE__).eval(:(^(x...) = unchecked_pow(x...)))
4754
(@__MODULE__).eval(:(abs(x) = unchecked_abs(x)))
55+
(@__MODULE__).eval(:(÷(x...) = unchecked_div(x...)))
56+
(@__MODULE__).eval(:(div(x) = unchecked_div(x)))
57+
(@__MODULE__).eval(:(fld(x) = unchecked_fld(x)))
58+
(@__MODULE__).eval(:(cld(x) = unchecked_cld(x)))
59+
(@__MODULE__).eval(:(%(x...) = unchecked_mod(x...)))
60+
(@__MODULE__).eval(:(rem(x) = unchecked_rem(x)))
61+
(@__MODULE__).eval(:(mod(x) = unchecked_mod(x)))
4862
(@__MODULE__).eval(:(__OverflowContextDefaultSet = true))
4963
nothing
5064
end
@@ -69,6 +83,13 @@ macro default_saturating()
6983
(@__MODULE__).eval(:(*(x...) = OverflowContexts.saturating_mul(x...)))
7084
(@__MODULE__).eval(:(^(x...) = OverflowContexts.saturating_pow(x...)))
7185
(@__MODULE__).eval(:(abs(x) = OverflowContexts.saturating_abs(x)))
86+
(@__MODULE__).eval(:(÷(x...) = OverflowContexts.saturating_div(x...)))
87+
(@__MODULE__).eval(:(div(x) = OverflowContexts.saturating_div(x)))
88+
(@__MODULE__).eval(:(fld(x) = OverflowContexts.saturating_fld(x)))
89+
(@__MODULE__).eval(:(cld(x) = OverflowContexts.saturating_cld(x)))
90+
(@__MODULE__).eval(:(%(x...) = OverflowContexts.saturating_mod(x...)))
91+
(@__MODULE__).eval(:(rem(x) = OverflowContexts.saturating_rem(x)))
92+
(@__MODULE__).eval(:(mod(x) = OverflowContexts.saturating_mod(x)))
7293
(@__MODULE__).eval(:(__OverflowContextDefaultSet = true))
7394
nothing
7495
end
@@ -115,6 +136,13 @@ const op_checked = Dict(
115136
:* => :(checked_mul),
116137
:^ => :(checked_pow),
117138
:abs => :(checked_abs),
139+
:÷ => :(checked_div),
140+
:div => :(checked_div),
141+
:fld => :(checked_fld),
142+
:cld => :(checked_cld),
143+
:% => :(checked_rem),
144+
:rem => :(checked_rem),
145+
:mod => :(checked_mod)
118146
)
119147

120148
const op_unchecked = Dict(
@@ -124,7 +152,14 @@ const op_unchecked = Dict(
124152
:- => :(unchecked_sub),
125153
:* => :(unchecked_mul),
126154
:^ => :(unchecked_pow),
127-
:abs => :(unchecked_abs)
155+
:abs => :(unchecked_abs),
156+
:÷ => :(unchecked_div),
157+
:div => :(unchecked_div),
158+
:fld => :(unchecked_fld),
159+
:cld => :(unchecked_cld),
160+
:% => :(unchecked_rem),
161+
:rem => :(unchecked_rem),
162+
:mod => :(unchecked_mod)
128163
)
129164

130165
const op_saturating = Dict(
@@ -134,25 +169,38 @@ const op_saturating = Dict(
134169
:- => :(saturating_sub),
135170
:* => :(saturating_mul),
136171
:^ => :(saturating_pow),
137-
:abs => :(saturating_abs)
172+
:abs => :(saturating_abs),
173+
:÷ => :(saturating_div),
174+
:div => :(saturating_div),
175+
:fld => :(saturating_fld),
176+
:cld => :(saturating_cld),
177+
:% => :(saturating_rem),
178+
:rem => :(saturating_rem),
179+
:mod => :(saturating_mod)
138180
)
139181

140182
const broadcast_op_map = Dict(
141183
:.+ => :+,
142184
:.- => :-,
143185
:.* => :*,
144-
:.^ => :^
186+
:.^ => :^,
187+
: => :÷,
188+
:.% => :%
145189
)
146190

147191
const assignment_op_map = Dict(
148192
:+= => :+,
149193
:-= => :-,
150194
:*= => :*,
151195
:^= => :^,
196+
:÷= => :÷,
197+
:%= => :%,
152198
:.+= => :.+,
153199
:.-= => :.-,
154200
:.*= => :.*,
155201
:.^= => :.^,
202+
:.÷= => :,
203+
:.%= => :.%
156204
)
157205

158206
# resolve ambiguity when `-` used as symbol

0 commit comments

Comments
 (0)