diff --git a/.github/workflows/UnitTest.yml b/.github/workflows/UnitTest.yml index fa28a12..89843cf 100644 --- a/.github/workflows/UnitTest.yml +++ b/.github/workflows/UnitTest.yml @@ -31,6 +31,9 @@ jobs: os: [ubuntu-latest, windows-latest, macos-15-intel] julia-arch: [x64] include: + - os: ubuntu-latest # the minimum supported version + julia-version: '1.6' + julia-arch: x64 - os: ubuntu-latest # only test one 32-bit job julia-version: '1' julia-arch: x86 diff --git a/Project.toml b/Project.toml index d7afc92..199cacd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "FixedPointNumbers" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.9.2" +version = "0.10.0" [deps] Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -15,11 +15,11 @@ FixedPointNumbersStatisticsExt = "Statistics" [compat] Aqua = "0.8" Documenter = "0.27, 1" -Random = "<0.0.1, 1" +Random = "1" StableRNGs = "1" Statistics = "1" Test = "1" -julia = "1" +julia = "1.6" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 7bafaca..5105918 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -6,7 +6,7 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox, zero, oneunit, one, typemin, typemax, floatmin, floatmax, eps, reinterpret, big, rationalize, float, trunc, round, floor, ceil, bswap, clamp, div, fld, cld, rem, mod, mod1, fld1, min, max, minmax, - signed, unsigned, sign, copysign, flipsign, signbit, + sign, copysign, flipsign, signbit, length, widen, widemul import Random: Random, AbstractRNG, SamplerType, rand! @@ -137,8 +137,7 @@ zero(::Type{X}) where {X <: FixedPoint} = X(zero(rawtype(X)), 0) oneunit(::Type{X}) where {X <: FixedPoint} = X(rawone(X), 0) one(::Type{X}) where {X <: FixedPoint} = oneunit(X) -# for Julia v1.0, which does not fold `div_float` before inlining -inv_rawone(x) = (@generated) ? (y = 1.0 / rawone(x); :($y)) : 1.0 / rawone(x) +inv_rawone(x) = 1.0 / rawone(x) # traits eps(::Type{X}) where {X <: FixedPoint} = X(oneunit(rawtype(X)), 0) @@ -357,36 +356,24 @@ bitstring(x::FixedPoint) = bitstring(x.i) bswap(x::X) where {X <: FixedPoint} = sizeof(X) == 1 ? x : X(bswap(x.i), 0) -# At least on Julia v1.5.0 or earlier, the following specialization helps the -# SIMD vectorization. (cf. PR #194) +# Julia 1.10 and earlier vectorize the loops over `clamp` only with this specialization. (cf. PR #194) clamp(x::X, lo::X, hi::X) where {X <: FixedPoint} = X(clamp(x.i, lo.i, hi.i), 0) clamp(x, ::Type{X}) where {X <: FixedPoint} = clamp(x, typemin(X), typemax(X)) % X -# Since `FixedPoint` is not an integer type, it is not clear in what type -# `signed` and `unsigned` for `FixedPoint` should return values. They should -# currently throw errors in case we support "unsigned Fixed" or "signed Normed" -# in the future. The following "incomplete" code is necessary for Julia v1.0 -# etc. to prevent accidental conversion to an integer type. -signed(x::X) where {X <: FixedPoint} = signed(X)(signed(x.i), 0) -unsigned(x::X) where {X <: FixedPoint} = unsigned(X)(unsigned(x.i), 0) - function sign(x::X) where {T, X <: FixedPoint{T}} T <: Unsigned && return iszero(x) ? x : oneunit(X) # `Q0f7`, etc., result in an error for `oneunit(X)`. x <= zero(X) && return x === zero(X) ? x : X(-1) return oneunit(X) end + function copysign(x::X, y::Real) where {T, X <: FixedPoint{T}} T <: Signed ? X(copysign(x.i, y), 0) : throw_not_a_signed_number_error(x) end function flipsign(x::X, y::Real) where {T, X <: FixedPoint{T}} T <: Signed ? X(flipsign(x.i, y), 0) : throw_not_a_signed_number_error(x) end -if copysign(-1, 0x1) !== 1 # for Julia v1.0 and v1.1 (julia #30748) - copysign(x::X, y::Unsigned) where {T, X <: FixedPoint{T}} = copysign(x, signed(y)) - flipsign(x::X, y::Unsigned) where {T, X <: FixedPoint{T}} = flipsign(x, signed(y)) -end @noinline function throw_not_a_signed_number_error(x) throw(ArgumentError("$x is not a signed number.")) end @@ -515,16 +502,6 @@ function show(io::IO, x::FixedPoint{T,f}) where {T,f} return nothing end -if VERSION < v"1.6.0-DEV.356" # JuliaLang/julia#36107 - function Base.showarg(io::IO, a::Array{X}, toplevel) where {X<:FixedPoint} - toplevel || print(io, "::") - print(io, "Array{") - showtype(io, X) - print(io, ",$(ndims(a))}") - toplevel && hasalias(X) && print(io, " with eltype ", X) - end -end - include("fixed.jl") include("normed.jl") include("deprecations.jl") @@ -666,9 +643,7 @@ if !isdefined(Base, :get_extension) include("../ext/FixedPointNumbersStatisticsExt.jl") end -if VERSION >= v"1.1" # work around https://github.com/JuliaLang/julia/issues/34121 - include("precompile.jl") - _precompile_() -end +include("precompile.jl") +_precompile_() end # module diff --git a/src/fixed.jl b/src/fixed.jl index 212a95f..2a3971d 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -50,7 +50,7 @@ fracmask(x::Fixed{T,f}) where {T, f} = ~intmask(x) # Signed # constructor-style conversions function _convert(::Type{F}, x::Fixed{T2,f2}) where {T, T2, f, f2, F <: Fixed{T,f}} - y = round(@exp2(f-f2) * reinterpret(x)) + y = round(exp2(f-f2) * reinterpret(x)) (typemin(T) <= y) & (y <= typemax(T)) || throw_converterror(F, x) reinterpret(F, _unsafe_trunc(T, y)) end @@ -65,10 +65,10 @@ end function _convert(::Type{F}, x::AbstractFloat) where {T, f, F <: Fixed{T,f}} bigx = big(x) - bmin = BigFloat(typemin(F)) - @exp2(-f-1) - bmax = BigFloat(typemax(F)) + @exp2(-f-1) + bmin = BigFloat(typemin(F)) - exp2(-f-1) + bmax = BigFloat(typemax(F)) + exp2(-f-1) if bmin <= bigx < bmax - reinterpret(F, round(T, bigx * @exp2(f))) + reinterpret(F, round(T, bigx * exp2(f))) else throw_converterror(F, x) end @@ -78,8 +78,8 @@ _convert(::Type{F}, x::Float16) where {T, f, F <: Fixed{T,f}} = F(Float32(x)) function _convert(::Type{F}, x::Union{Float32, Float64}) where {T, f, F <: Fixed{T,f}} Tf = typeof(x) - if Tf(typemin(F) - @exp2(-f-1)) <= x < Tf(typemax(F) + @exp2(-f-1)) - reinterpret(F, round(T, x * @exp2(f))) + if Tf(typemin(F) - exp2(-f-1)) <= x < Tf(typemax(F) + exp2(-f-1)) + reinterpret(F, round(T, x * exp2(f))) else throw_converterror(F, x) end @@ -89,7 +89,7 @@ function _convert(::Type{F}, x::Rational) where {T, f, F <: Fixed{T,f}} xmin = widemul(denominator(x), widen1(T)(typemin(T)) << 0x1 - 0x1) xmax = widemul(denominator(x), oneunit(widen1(T)) << bitwidth(T) - 0x1) if xmin <= (widen1(numerator(x)) << UInt8(f + 1)) < xmax - reinterpret(F, round(T, convert(floattype(T), x) * @exp2(f))) + reinterpret(F, round(T, convert(floattype(T), x) * exp2(f))) else throw_converterror(F, x) end @@ -98,7 +98,7 @@ end _rem(x::F, ::Type{F}) where {F <: Fixed} = x function _rem(x::Fixed, ::Type{F}) where {T, f, F <: Fixed{T,f}} f2 = nbitsfrac(typeof(x)) - y = round(@exp2(f - f2) * reinterpret(x)) + y = round(exp2(f - f2) * reinterpret(x)) reinterpret(F, _unsafe_trunc(T, y)) end _rem(x::Integer, ::Type{F}) where {T, f, F <: Fixed{T,f}} = F(_unsafe_trunc(T, x) << f, 0) @@ -110,18 +110,18 @@ function _rem(x::Real, ::Type{F}) where {T, f, F <: Fixed{T,f}} Ti = promote_type(Int64, T) end Tf = floattype(F) - y = _unsafe_trunc(Ti, round(x * Tf(@exp2(f)))) + y = _unsafe_trunc(Ti, round(x * Tf(exp2(f)))) reinterpret(F, _unsafe_trunc(T, y)) end function _rem(x::BigFloat, ::Type{F}) where {T, f, F <: Fixed{T,f}} isfinite(x) || return zero(F) - reinterpret(F, _unsafe_trunc(T, round(x * @exp2(f)))) + reinterpret(F, _unsafe_trunc(T, round(x * exp2(f)))) end -(::Type{Tf})(x::Fixed{T,f}) where {Tf <: AbstractFloat, T, f} = Tf(Tf(x.i) * Tf(@exp2(-f))) +(::Type{Tf})(x::Fixed{T,f}) where {Tf <: AbstractFloat, T, f} = Tf(Tf(x.i) * Tf(exp2(-f))) Base.Float16(x::Fixed{T,f}) where {T, f} = Float16(Float32(x)) -Base.Float32(x::Fixed{T,f}) where {T, f} = Float32(x.i) * Float32(@exp2(-f)) -Base.Float64(x::Fixed{T,f}) where {T, f} = Float64(x.i) * @exp2(-f) +Base.Float32(x::Fixed{T,f}) where {T, f} = Float32(x.i) * Float32(exp2(-f)) +Base.Float64(x::Fixed{T,f}) where {T, f} = Float64(x.i) * exp2(-f) function Base.Rational(x::Fixed{T,f}) where {T, f} f < bitwidth(T)-1 ? x.i//rawone(x) : x.i//(one(widen1(T))<>> 0x1 lower = intmask(x) >> 0x1 if f == bitwidth(T) - 1 - x.i > upper && throw_converterror(Fixed{T,f}, @exp2(bitwidth(T)-f-1)) + x.i > upper && throw_converterror(Fixed{T,f}, exp2(bitwidth(T)-f-1)) return x.i < lower ? typemin(x) : zero(x) end - x.i >= upper && throw_converterror(Fixed{T,f}, @exp2(bitwidth(T)-f-1)) + x.i >= upper && throw_converterror(Fixed{T,f}, exp2(bitwidth(T)-f-1)) y = oneunit(T) << UInt8(f - 1) + x.i m = oneunit(T) << UInt8(f + 1) - oneunit(T) z = y & intmask(x) @@ -223,7 +223,7 @@ function _round_digits(x::F, r::RoundingMode, d::Int) where {f, F <: Fixed{Int8, else xr = r isa RoundingMode{:Up} ? (xd + fracmask(x)) >> uf : div_2f(xd, Val(Int(f))) if d < 3 && xr == (Int16(d === 1 ? 10 : 100) << (0x7 - uf)) - throw_converterror(F, @exp2(7 - f)) + throw_converterror(F, exp2(7 - f)) end end h8, h24 = Int16(0x80), Int32(1 << 23) diff --git a/src/normed.jl b/src/normed.jl index 21ad969..f3931ba 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -157,8 +157,7 @@ function Base.Float32(x::Normed{UInt8,f}) where f f == 5 && return Float32(Int32(x.i) * 0x003) * @f32(0xb02c0bp-30) f == 6 && return Float32(Int32(x.i) * 0x049) * @f32(0xe40039p-36) f == 7 && return Float32(Int32(x.i) * 0x01f) * @f32(0x852b5fp-35) - f == 8 && return Float32(Int32(x.i) * 0x155) * @f32(0xc0f0fdp-40) - 0.0f0 + #= f == 8 =# return Float32(Int32(x.i) * 0x155) * @f32(0xc0f0fdp-40) end function Base.Float32(x::Normed{UInt16,f}) where f f32 = Float32(x.i) @@ -180,7 +179,7 @@ function Base.Float32(x::Normed{UInt32,f}) where f return muladd(rh, @f32(0x1p-16), rl * @f32(0x1p-40)) elseif f >= 25 rh, rl = Float32(i32>>>0x10), Float32((i32&0xFFFF)<<0xE + i32>>>UInt8(f-14)) - return muladd(rh, Float32(@exp2(16-f)), rl * Float32(@exp2(-14-f))) + return muladd(rh, Float32(exp2(16-f)), rl * Float32(exp2(-14-f))) end # FIXME: avoid the branch in native x86_64 (non-SIMD) codes m = ifelse(i32 < 0, 0x1p32 * inv_rawone(x), 0.0) @@ -226,24 +225,24 @@ end function Base.Float64(x::Normed{UInt64,f}) where f f == 1 && return Float64(x.i) if f >= 53 - rh = Float64(unsafe_trunc(Int64, x.i>>0x10)) * @exp2(16-f) # upper 48 bits - rl = Float64(unsafe_trunc(Int32, x.i&0xFFFF)) * @exp2(-f) # lower 16 bits - return rh + muladd(rh, @exp2(-f), rl) + rh = Float64(unsafe_trunc(Int64, x.i>>0x10)) * exp2(16-f) # upper 48 bits + rl = Float64(unsafe_trunc(Int32, x.i&0xFFFF)) * exp2(-f) # lower 16 bits + return rh + muladd(rh, exp2(-f), rl) end x.i / rawone(x) end function Base.Float64(x::Normed{UInt128,f}) where f f == 1 && return Float64(x.i) ih, il = unsafe_trunc(Int64, x.i>>0x40), unsafe_trunc(Int64, x.i) - rh = Float64(ih>>>0x10) * @exp2(f <= 53 ? 80 : 80 - f) # upper 48 bits - km = @exp2(f <= 53 ? 48 : 48 - f) # for middle 32 bits + rh = Float64(ih>>>0x10) * exp2(f <= 53 ? 80 : 80 - f) # upper 48 bits + km = exp2(f <= 53 ? 48 : 48 - f) # for middle 32 bits rm = Float64(unsafe_trunc(Int32, ih&0xFFFF)) * (0x1p16 * km) + Float64(unsafe_trunc(Int32, il>>>0x30)) * km - rl = Float64(il&0xFFFFFFFFFFFF) * @exp2(f <= 53 ? 0 : -f) # lower 48 bits + rl = Float64(il&0xFFFFFFFFFFFF) * exp2(f <= 53 ? 0 : -f) # lower 48 bits if f <= 53 return (rh + (rm + rl)) / unsafe_trunc(Int64, rawone(x)) elseif f < 76 - return rh + (rm + muladd(rh, @exp2(-f), rl)) + return rh + (rm + muladd(rh, exp2(-f), rl)) else return rh + (rm + rl) end diff --git a/src/utilities.jl b/src/utilities.jl index cb7650d..0ee4cfe 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -26,10 +26,6 @@ const UShorterThanInt = typeintersect(ShorterThanInt, Unsigned) macro f32(x::Float64) # just for hexadecimal floating-point literals :(Float32($x)) end -macro exp2(n) - :(_exp2(Val($(esc(n))))) -end -_exp2(::Val{N}) where {N} = exp2(N) # these are defined in julia/float.jl or julia/math.jl, but not exported significand_bits(::Type{Float32}) = 23 diff --git a/test/common.jl b/test/common.jl index def7f6b..36c723a 100644 --- a/test/common.jl +++ b/test/common.jl @@ -2,8 +2,6 @@ using FixedPointNumbers, Statistics, Random, StableRNGs, Test using FixedPointNumbers: bitwidth, rawtype, nbitsfrac using Base.Checked -SP = VERSION >= v"1.6.0-DEV.771" ? " " : "" # JuliaLang/julia #37085 - """ target(X::Type, Ss...; ex = :default) @@ -153,11 +151,32 @@ function test_convert_from_nan(TX::Type) end end +function test_hash_rational(TX::Type) + @testset "hash(::$X)" for X in target(TX, :i8, :i16, :i32, :i64; ex = :thin) + for x in (zero(X), eps(X), typemin(X), typemax(X)) + @test x == Rational(x) + @test hash(x) == hash(Rational(x)) + end + end +end + +# a `Real` type with only the methods that `x % X` needs +struct IntReal <: Real + i::Int +end +Base.:*(a::Integer, x::IntReal) = IntReal(a * x.i) +Base.:*(x::IntReal, a::AbstractFloat) = IntReal(Int(x.i * a)) +Base.isfinite(::IntReal) = true +Base.round(x::IntReal) = x +Base.unsafe_trunc(::Type{T}, x::IntReal) where {T <: Integer} = x.i % T + function test_rem_type(TX::Type) @testset "% $X" for X in target(TX, :i8, :i16; ex = :thin) xs = typemin(X):0.1:typemax(X) @test all(x -> x % X === X(x), xs) @test wrapping_rem(2, X) === saturating_rem(2, X) === checked_rem(2, X) === 2 % X + @test IntReal(1) % X === 1 % X + @test IntReal(2) % X === 2 % X end end @@ -378,10 +397,10 @@ end function test_sign_related(TX::Type) for X in target(TX, :i8; ex = :thin) # `FixedPoint` is not a signed or unsigned integer type - @test_throws Exception signed(X) - @test_throws Exception signed(X(0.5)) - @test_throws Exception unsigned(X) - @test_throws Exception unsigned(X(0.5)) + @test_throws MethodError signed(X) + @test_throws MethodError signed(X(0.5)) + @test_throws MethodError unsigned(X) + @test_throws MethodError unsigned(X(0.5)) @test sign(X(0.0)) === zero(X) if typemax(X) >= 1.0 @@ -400,6 +419,16 @@ function test_isinteger(TX::Type) end end +function test_reductions_short(TX::Type) + @testset "sum/prod(::Vector{$X})" for X in target(TX; ex = :thin) + x = typemax(X) + @test sum(X[]) === 0.0 + @test prod(X[]) === 1.0 + @test sum([x]) === Float64(x) + @test prod([x]) === Float64(x) + end +end + function test_rand(TX::Type) @testset "rand(::$X)" for X in target(TX; ex = :thin) @test isa(rand(X), X) diff --git a/test/fixed.jl b/test/fixed.jl index d0909c3..c1ea4b9 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -172,7 +172,7 @@ end @test occursin("Q0f7 is an 8-bit type representing 256 values from -1.0 to 0.992;", msg) ret = @test_throws ArgumentError convert(Fixed{Int128,100}, 10.0^9) msg = ret.value.msg - @test occursin("Fixed{Int128,$(SP)100} is a 128-bit type representing 2^128 values", msg) + @test occursin("Fixed{Int128, 100} is a 128-bit type representing 2^128 values", msg) end @testset "disambiguation constructors" begin @@ -229,6 +229,8 @@ end @test rationalize(Int16, 0.81Q3f4) === Rational{Int16}(13//16) @test rationalize(-0.81Q3f4, tol=0.02) === Rational{Int}(-13//16) @test rationalize(Int8, -0.81Q3f4, tol=0.07) === Rational{Int8}(-3//4) + + test_hash_rational(Fixed) end @testset "BigFloat conversions" begin @@ -403,6 +405,12 @@ end end test_mul(Fixed) + # `f == 0` has its own overflow bounds + @test checked_mul(Q7f0(127), Q7f0(1)) === Q7f0(127) + @test checked_mul(Q7f0(-64), Q7f0(2)) === Q7f0(-128) + @test_throws OverflowError checked_mul(Q7f0(64), Q7f0(2)) + @test_throws OverflowError checked_mul(Q7f0(-128), Q7f0(-1)) + FixedPointNumbers.mul_with_rounding(1.5Q6f1, 0.5Q6f1, RoundNearest) === 1.0Q6f1 FixedPointNumbers.mul_with_rounding(1.5Q6f1, -0.5Q6f1, RoundNearest) === -1.0Q6f1 FixedPointNumbers.mul_with_rounding(1.5Q6f1, 0.5Q6f1, RoundNearestTiesUp) === 1.0Q6f1 @@ -694,6 +702,8 @@ end acmp = Float64(a[1])*Float64(a[2]) @test prod(a) == acmp @test prod(a, dims=1) == [acmp] + + test_reductions_short(Fixed) end @testset "reductions, Statistics" begin @@ -801,24 +811,18 @@ end @test String(take!(iob)) == "-21845.33334Q15f16" show(iob, Fixed{Int128,64}(-1.2345e6)) - @test String(take!(iob)) == "Fixed{Int128,$(SP)64}(-1.2345e6)" + @test String(take!(iob)) == "Fixed{Int128, 64}(-1.2345e6)" # TODO: remove this test show(iob, reinterpret(Fixed{Int8,8}, signed(0xaa))) - @test String(take!(iob)) == "Fixed{Int8,$(SP)8}(-0.336)" + @test String(take!(iob)) == "Fixed{Int8, 8}(-0.336)" end @testset "summary" begin a = Q0f7[0.2, 0.4] aa = Fixed[0.2Q0f7 0.4Q0f15] - if VERSION >= v"1.6.0-DEV.356" - @test summary(a) == "2-element Vector{Q0f7}" - @test summary(view(a, 1:2)) == "2-element view(::Vector{Q0f7}, 1:2) with eltype Q0f7" - @test summary(aa) == "1×2 Matrix{Fixed}" - else - @test summary(a) == "2-element Array{Q0f7,1} with eltype Fixed{Int8,7}" - @test summary(view(a, 1:2)) == "2-element view(::Array{Q0f7,1}, 1:2) with eltype Fixed{Int8,7}" - @test summary(aa) == "1×2 Array{Fixed,2}" - end + @test summary(a) == "2-element Vector{Q0f7}" + @test summary(view(a, 1:2)) == "2-element view(::Vector{Q0f7}, 1:2) with eltype Q0f7" + @test summary(aa) == "1×2 Matrix{Fixed}" end diff --git a/test/normed.jl b/test/normed.jl index 50b6009..70e32d3 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -116,7 +116,7 @@ end @test occursin("N0f8 is an 8-bit type representing 256 values from 0.0 to 1.0;", msg) ret = @test_throws ArgumentError convert(Normed{UInt128,100}, 10.0^9) msg = ret.value.msg - @test occursin("Normed{UInt128,$(SP)100} is a 128-bit type representing 2^128 values", msg) + @test occursin("Normed{UInt128, 100} is a 128-bit type representing 2^128 values", msg) end @testset "disambiguation constructors" begin @@ -193,6 +193,8 @@ end @test rationalize(Int16, 0.804N0f8) === Rational{Int16}(41//51) @test rationalize(0.804N0f8, tol=0.002) === Rational{Int}(41//51) @test rationalize(Int8, 0.804N0f8, tol=0.005) === Rational{Int8}(4//5) + + test_hash_rational(Normed) end @testset "BigFloat conversions" begin @@ -230,7 +232,6 @@ end end input_upper = Tf(BigFloat(typemax(T)) / r, RoundDown) - isinf(input_upper) && continue # for Julia v0.7 @test reinterpret(N(input_upper)) == T(min(round(BigFloat(input_upper) * r), typemax(T))) input_exp2 = Tf(exp2(bitwidth(T) - f)) @@ -252,6 +253,11 @@ end @test isa(y, T) end + # the generic method serves `AbstractFloat` types without a dedicated method + for Tf in (Float16, Float32, Float64), x in (0.2N0f8, 0.2N0f16, 0.2N2f14, 0.2N0f32, 0.2N0f64) + @test invoke(Tf, Tuple{Normed}, x) === Tf(x) + end + for Tf in (Float16, Float32, Float64) @testset "$Tf(::$N)" for N in target(Normed, :i8, :i16) T = rawtype(N) @@ -672,6 +678,8 @@ end acmp = Float64(a[1])*Float64(a[2]) @test prod(a) == acmp @test prod(a, dims=1) == [acmp] + + test_reductions_short(Normed) end @testset "reductions, Statistics" begin @@ -779,22 +787,16 @@ end @test String(take!(iob)) == "43691.33333N16f16" show(iob, Normed{UInt128,64}(1.2345e6)) - @test String(take!(iob)) == "Normed{UInt128,$(SP)64}(1.2345e6)" + @test String(take!(iob)) == "Normed{UInt128, 64}(1.2345e6)" end @testset "summary" begin a = N0f8[0.2, 0.4] aa = Normed[0.2N0f8 0.4N0f16] - if VERSION >= v"1.6.0-DEV.356" - @test summary(a) == "2-element Vector{N0f8}" - @test summary(view(a, 1:2)) == "2-element view(::Vector{N0f8}, 1:2) with eltype N0f8" - @test summary(aa) == "1×2 Matrix{Normed}" - else - @test summary(a) == "2-element Array{N0f8,1} with eltype Normed{UInt8,8}" - @test summary(view(a, 1:2)) == "2-element view(::Array{N0f8,1}, 1:2) with eltype Normed{UInt8,8}" - @test summary(aa) == "1×2 Array{Normed,2}" - end + @test summary(a) == "2-element Vector{N0f8}" + @test summary(view(a, 1:2)) == "2-element view(::Vector{N0f8}, 1:2) with eltype N0f8" + @test summary(aa) == "1×2 Matrix{Normed}" end @testset "scaledual" begin diff --git a/test/traits.jl b/test/traits.jl index c248b4c..a99621d 100644 --- a/test/traits.jl +++ b/test/traits.jl @@ -17,4 +17,6 @@ struct MyReal <: Real end @test floattype(typeof(π)) === Float64 @test_skip(@test_throws MethodError floattype(MyReal)) # TODO: eliminate `@test_skipped` when depwarn is eliminated. See #177. + # meanwhile, the deprecated fallback warns and returns the type unchanged + @test (@test_logs (:warn, r"fallback definition of `floattype`") floattype(MyReal)) === MyReal end