Skip to content

Commit

Permalink
do not add methods to promote_type
Browse files Browse the repository at this point in the history
  • Loading branch information
bvdmitri committed Apr 22, 2024
1 parent 866e52e commit 9d45b53
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 24 deletions.
56 changes: 35 additions & 21 deletions src/TinyHugeNumbers.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module TinyHugeNumbers

export tiny, huge
export tiny, huge

import Base: show, convert, promote_rule

Expand All @@ -11,18 +11,18 @@ struct TinyNumber <: Real end

Base.show(io::IO, ::TinyNumber) = print(io, "tiny")

Base.convert(::Type{F}, ::TinyNumber) where {F <: AbstractFloat} = 10eps(F)
Base.convert(::Type{Float32}, ::TinyNumber) = 1.0f-6
Base.convert(::Type{Float64}, ::TinyNumber) = 1e-12
Base.convert(::Type{F}, ::TinyNumber) where {F<:AbstractFloat} = 10eps(F)
Base.convert(::Type{Float32}, ::TinyNumber) = 1.0f-6
Base.convert(::Type{Float64}, ::TinyNumber) = 1e-12
Base.convert(::Type{BigFloat}, ::TinyNumber) = big"1e-24"

Base.promote_rule(::Type{TinyNumber}, ::Type{I}) where {I <: Integer} = promote_rule(TinyNumber, promote_type(I, Float64))
Base.promote_rule(::Type{TinyNumber}, ::Type{F}) where {F <: AbstractFloat} = F
Base.promote_rule(::Type{TinyNumber}, ::Type{I}) where {I<:Integer} = promote_type(TinyNumber, promote_type(I, Float64))
Base.promote_rule(::Type{TinyNumber}, ::Type{F}) where {F<:AbstractFloat} = F

(::Type{T})(::TinyNumber) where {T <: AbstractFloat} = convert(T, tiny)
(::Type{T})(::TinyNumber) where {T<:AbstractFloat} = convert(T, tiny)

(::TinyNumber)(::Type{ T }) where {T} = convert(T, TinyNumber())
(::TinyNumber)(something) = convert(typeof(something), TinyNumber())
(::TinyNumber)(::Type{T}) where {T} = convert(T, TinyNumber())
(::TinyNumber)(something) = convert(typeof(something), TinyNumber())

"""
tiny
Expand Down Expand Up @@ -69,18 +69,18 @@ struct HugeNumber <: Real end

Base.show(io::IO, ::HugeNumber) = print(io, "huge")

Base.convert(::Type{F}, ::HugeNumber) where {F <: AbstractFloat} = inv(tiny(F))
Base.convert(::Type{Float32}, ::HugeNumber) = 1.0f+6
Base.convert(::Type{Float64}, ::HugeNumber) = 1e+12
Base.convert(::Type{F}, ::HugeNumber) where {F<:AbstractFloat} = inv(tiny(F))
Base.convert(::Type{Float32}, ::HugeNumber) = 1.0f+6
Base.convert(::Type{Float64}, ::HugeNumber) = 1e+12
Base.convert(::Type{BigFloat}, ::HugeNumber) = big"1e+24"

Base.promote_rule(::Type{HugeNumber}, ::Type{I}) where {I <: Integer} = promote_rule(HugeNumber, promote_type(I, Float64))
Base.promote_rule(::Type{HugeNumber}, ::Type{F}) where {F <: AbstractFloat} = F
Base.promote_rule(::Type{HugeNumber}, ::Type{I}) where {I<:Integer} = promote_type(HugeNumber, promote_type(I, Float64))
Base.promote_rule(::Type{HugeNumber}, ::Type{F}) where {F<:AbstractFloat} = F

(::Type{T})(::HugeNumber) where {T <: AbstractFloat} = convert(T, huge)
(::Type{T})(::HugeNumber) where {T<:AbstractFloat} = convert(T, huge)

(::HugeNumber)(::Type{ T }) where {T} = convert(T, HugeNumber())
(::HugeNumber)(something) = convert(typeof(something), HugeNumber())
(::HugeNumber)(::Type{T}) where {T} = convert(T, HugeNumber())
(::HugeNumber)(something) = convert(typeof(something), HugeNumber())

"""
huge
Expand Down Expand Up @@ -120,9 +120,23 @@ const huge = HugeNumber()

## ------------------------------------------------------------------------------------ ##

Base.promote_type(::Type{T}, ::Type{TinyNumber}, ::Type{HugeNumber}) where {T} =
promote_type(promote_type(T, TinyNumber), HugeNumber)
Base.promote_type(::Type{T}, ::Type{HugeNumber}, ::Type{TinyNumber}) where {T} =
promote_type(promote_type(T, HugeNumber), TinyNumber)
# A special structure that is used to promote `TinyNumber` and `HugeNumber` to the same type
# but it cannot be instantiated, this might be useful in situations like `clamp(value, tiny, huge)`
# in this case Julia attempts first to promote `tiny` and `huge` to the same type and then
# uses the result to promote `value` to the resulting type. However, there is no "common" type for
# both `tiny` and `huge` so we introduce a special structure that will accomodate that
# see also: https://github.com/ReactiveBayes/TinyHugeNumbers.jl/issues/3
struct PromoteTinyOrHuge end

PromoteTinyOrHuge() = error("Cannot instantiate an internal structure for promotion.")

Base.promote_rule(::Type{T}, ::Type{PromoteTinyOrHuge}) where {T} = T
Base.promote_rule(::Type{PromoteTinyOrHuge}, ::Type{PromoteTinyOrHuge}) = PromoteTinyOrHuge
Base.promote_rule(::Type{TinyNumber}, ::Type{PromoteTinyOrHuge}) = PromoteTinyOrHuge
Base.promote_rule(::Type{HugeNumber}, ::Type{PromoteTinyOrHuge}) = PromoteTinyOrHuge
Base.promote_rule(::Type{TinyNumber}, ::Type{HugeNumber}) = PromoteTinyOrHuge

Base.convert(::Type{PromoteTinyOrHuge}, ::TinyNumber) = error("Cannot convert `tiny` to `huge`. Are you trying to put `tiny` and `huge` in the same container (e.g. `Array`)?")
Base.convert(::Type{PromoteTinyOrHuge}, ::HugeNumber) = error("Cannot convert `huge` to `tiny`. Are you trying to put `tiny` and `huge` in the same container (e.g. `Array`)?")

end
9 changes: 6 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,16 @@ struct ArbitraryFloatType <: AbstractFloat end
@test @inferred clamp(big"1e25", tiny, huge) == big"1e+24"

for T in (Float64, Float32, BigFloat, ArbitraryFloatType)
@test promote_type(T, TinyNumber, HugeNumber) == T
@test promote_type(T, HugeNumber, TinyNumber) == T
@test @inferred(promote_type(T, TinyNumber, HugeNumber)) == T
@test @inferred(promote_type(T, HugeNumber, TinyNumber)) == T
end

@test_throws "Cannot convert `tiny` to `huge`" [tiny, huge]
@test_throws "Cannot convert `huge` to `tiny`" [huge, tiny]

for a in (1, 1.0, 0, 0.0, 1.0f0, 0.0f0, Int32(0), Int32(1), big"1", big"1.0", big"0", big"0.0")
T = typeof(a)
for v in [tiny, huge]
for v in Real[tiny, huge]
V = typeof(v)

for op in [+, -, *, /, >, >=, <, <=]
Expand Down

0 comments on commit 9d45b53

Please sign in to comment.