diff --git a/.github/workflows/Invalidations.yml b/.github/workflows/Invalidations.yml index a582f3fa..d657cd16 100644 --- a/.github/workflows/Invalidations.yml +++ b/.github/workflows/Invalidations.yml @@ -27,7 +27,7 @@ jobs: lines = readlines("Project.toml") open("Project.toml", "w") do f for l in lines - if l == "version = \"0.9.0-dev\"" + if startswith(l, "version = ") l = "version = \"0.8.4\"" end println(f, l) @@ -46,7 +46,7 @@ jobs: lines = readlines("Project.toml") open("Project.toml", "w") do f for l in lines - if l == "version = \"0.9.0-dev\"" + if startswith(l, "version = ") l = "version = \"0.8.4\"" end println(f, l) diff --git a/Project.toml b/Project.toml index a76d1e6e..d8abe474 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "FixedPointNumbers" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.9.0" +version = "0.9.1" [deps] Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -17,9 +17,7 @@ Aqua = "0.8" Documenter = "0.27, 1" Random = "<0.0.1, 1" StableRNGs = "1" -# Update this version specifier when Statistics.jl v1.11.2 is released. -# https://github.com/JuliaStats/Statistics.jl/issues/165 -Statistics = "< 1.11.2" +Statistics = "1" Test = "1" julia = "1" diff --git a/src/fixed.jl b/src/fixed.jl index 1abd7ff4..417a511a 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -103,10 +103,10 @@ function _rem(x::Fixed, ::Type{F}) where {T, f, F <: Fixed{T,f}} end _rem(x::Integer, ::Type{F}) where {T, f, F <: Fixed{T,f}} = F(_unsafe_trunc(T, x) << f, 0) function _rem(x::Real, ::Type{F}) where {T, f, F <: Fixed{T,f}} + isfinite(x) || return zero(F) if bitwidth(T) < 32 Ti = T else - isfinite(x) || return zero(F) Ti = promote_type(Int64, T) end Tf = floattype(F) diff --git a/src/normed.jl b/src/normed.jl index a8fae420..eec16735 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -112,7 +112,7 @@ _rem(x::N, ::Type{N}) where {N <: Normed} = x _rem(x::Normed, ::Type{N}) where {T, N <: Normed{T}} = reinterpret(N, _unsafe_trunc(T, round((rawone(N)/rawone(x))*reinterpret(x)))) function _rem(x::Real, ::Type{N}) where {T, N <: Normed{T}} - bitwidth(T) < 32 || isfinite(x) || return zero(N) + isfinite(x) || return zero(N) reinterpret(N, _unsafe_trunc(T, round(rawone(N) * x))) end _rem(x::Float16, ::Type{X}) where {X <: Normed} = _rem(Float32(x), X) # avoid overflow @@ -283,8 +283,10 @@ end function checked_mul(x::N, y::N) where {T <: Union{UInt8,UInt16,UInt32,UInt64}, f, N <: Normed{T,f}} f == bitwidth(T) && return wrapping_mul(x, y) z = widemul(x.i, y.i) + # `rawone(N)` is odd, so `z` rounds to `typemax(N).i` (no overflow) up to + # and including `m`; overflow starts at `m + 1`. m = widemul(typemax(N).i, rawone(N)) + (rawone(N) >> 0x1) - z < m || throw_overflowerror(:*, x, y) + z <= m || throw_overflowerror(:*, x, y) N(div_2fm1(z, Val(Int(f))) % T, 0) end diff --git a/test/common.jl b/test/common.jl index fdd4cf7b..3a046080 100644 --- a/test/common.jl +++ b/test/common.jl @@ -162,9 +162,10 @@ function test_rem_type(TX::Type) end function test_rem_nan(TX::Type) - # TODO: avoid undefined behavior @testset "nan % $X" for X in target(TX, :i8, :i16, :i32, :i64; ex = :thin) @test NaN % X === NaN32 % X === NaN16 % X === zero(X) + @test Inf % X === Inf32 % X === Inf16 % X === zero(X) + @test -Inf % X === -Inf32 % X === -Inf16 % X === zero(X) end end diff --git a/test/normed.jl b/test/normed.jl index b6afb1d2..646b84b1 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -409,6 +409,10 @@ end @test_throws OverflowError checked_mul(typemax(N), typemax(N)) end end + # products whose exact value rounds to `typemax` must not throw + @test checked_mul(reinterpret(N1f7, 0x9c), reinterpret(N1f7, 0xd0)) === typemax(N1f7) + @test checked_mul(reinterpret(N7f9, 0x0201), reinterpret(N7f9, 0xff00)) === typemax(N7f9) + test_mul(Normed) end