Skip to content

Commit 81dabe1

Browse files
committed
Rebase Measurement's internal type to be Real-based, not AbstractFloat
1 parent f1d6e89 commit 81dabe1

File tree

8 files changed

+61
-59
lines changed

8 files changed

+61
-59
lines changed

src/Measurements.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ include("derivatives-type.jl")
5353
# measurement and propagate the uncertainty in the case of functions with
5454
# more than one argument (in order to deal with correlation between
5555
# arguments).
56-
struct Measurement{T<:AbstractFloat} <: AbstractFloat
56+
struct Measurement{T<:Real} <: AbstractFloat
5757
val::T
5858
err::T
5959
tag::UInt64
@@ -79,8 +79,8 @@ function Measurement{T}(::S) where {T, S}
7979
end
8080

8181
# Functions to quickly create an empty Derivatives object.
82-
@generated empty_der1(x::Measurement{T}) where {T<:AbstractFloat} = Derivatives{T}()
83-
@generated empty_der2(x::T) where {T<:AbstractFloat} = Derivatives{x}()
82+
@generated empty_der1(x::Measurement{T}) where {T<:Real} = Derivatives{T}()
83+
@generated empty_der2(x::T) where {T<:Real} = Derivatives{x}()
8484

8585
# Start from 1, 0 is reserved to derived quantities
8686
const tag_counter = Threads.Atomic{UInt64}(1)
@@ -91,9 +91,9 @@ function __init__()
9191
end
9292

9393
measurement(x::Measurement) = x
94-
measurement(val::T) where {T<:AbstractFloat} = Measurement(val, zero(T), UInt64(0), empty_der2(val))
95-
measurement(val::Real) = measurement(float(val))
96-
function measurement(val::T, err::T) where {T<:AbstractFloat}
94+
measurement(val::T) where {T<:AbstractFloat} = Measurement(val, zero(T), UInt64(0), empty_der2(val)) # FIXME
95+
measurement(val::Real) = measurement(float(val)) # FIXME
96+
function measurement(val::T, err::T) where {T<:AbstractFloat} # FIXME
9797
newder = empty_der2(val)
9898
if iszero(err)
9999
Measurement{T}(val, err, UInt64(0), newder)
@@ -102,7 +102,7 @@ function measurement(val::T, err::T) where {T<:AbstractFloat}
102102
return Measurement{T}(val, err, tag, Derivatives(newder, (val, err, tag)=>one(T)))
103103
end
104104
end
105-
measurement(val::Real, err::Real) = measurement(promote(float(val), float(err))...)
105+
measurement(val::Real, err::Real) = measurement(promote(float(val), float(err))...) # FIXME
106106
measurement(::Missing, ::Union{Real,Missing} = missing) = missing
107107
const ± = measurement
108108

src/conversions.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616
#
1717
### Code:
1818

19-
Base.convert(::Type{Measurement{T}}, a::Irrational) where {T<:AbstractFloat} =
19+
Base.convert(::Type{Measurement{T}}, a::Irrational) where {T<:Real} =
2020
measurement(T(a))::Measurement{T}
21-
Base.convert(::Type{Measurement{T}}, a::Rational{<:Integer}) where {T<:AbstractFloat} =
21+
Base.convert(::Type{Measurement{T}}, a::Rational{<:Integer}) where {T<:Real} =
2222
measurement(T(a))::Measurement{T}
23-
Base.convert(::Type{Measurement{T}}, a::Real) where {T<:AbstractFloat} =
23+
Base.convert(::Type{Measurement{T}}, a::Real) where {T<:Real} =
2424
measurement(T(a))::Measurement{T}
25-
Base.convert(::Type{Measurement{T}}, a::Base.TwicePrecision) where {T<:AbstractFloat} =
25+
Base.convert(::Type{Measurement{T}}, a::Base.TwicePrecision) where {T<:Real} =
2626
measurement(T(a))::Measurement{T}
27-
Base.convert(::Type{Measurement{T}}, a::AbstractChar) where {T<:AbstractFloat} =
27+
Base.convert(::Type{Measurement{T}}, a::AbstractChar) where {T<:Real} =
2828
measurement(T(a))::Measurement{T}
2929

3030
function Base.convert(::Type{Measurement{T}}, a::Complex) where {T}
@@ -35,9 +35,9 @@ function Base.convert(::Type{Measurement{T}}, a::Complex) where {T}
3535
end
3636
end
3737

38-
Base.convert(::Type{Measurement{T}}, a::Measurement{T}) where {T<:AbstractFloat} = a
38+
Base.convert(::Type{Measurement{T}}, a::Measurement{T}) where {T<:Real} = a
3939
function Base.convert(::Type{Measurement{T}},
40-
a::Measurement{<:AbstractFloat}) where {T<:AbstractFloat}
40+
a::Measurement{<:Real}) where {T<:Real}
4141
newder = empty_der2(zero(T))
4242
for tag in keys(a.der)
4343
newder = Derivatives(newder, (T(tag[1]), T(tag[2]), tag[3])=>T(a.der[tag]))
@@ -55,10 +55,10 @@ function Base.convert(::Type{Int}, a::Measurement)
5555
return convert(Int, a.val)::Int
5656
end
5757

58-
Base.promote_rule(::Type{Measurement{T}}, ::Type{S}) where {T<:AbstractFloat, S<:Real} =
58+
Base.promote_rule(::Type{Measurement{T}}, ::Type{S}) where {T<:Real, S<:Real} =
5959
Measurement{promote_type(T, S)}
6060
Base.promote_rule(::Type{Measurement{T}},
61-
::Type{Measurement{S}}) where {T<:AbstractFloat, S<:AbstractFloat} =
61+
::Type{Measurement{S}}) where {T<:Real, S<:Real} =
6262
Measurement{promote_type(T, S)}
6363

6464
# adaptation of JuliaLang/julia#30952

src/math.jl

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export @uncertain
3838
# σ_G = |σ_a·∂G/∂a|
3939
# The list of derivatives with respect to each measurement is updated with
4040
# ∂G/∂a · previous_derivatives
41-
@inline function result(val::T, der::Real, a::Measurement{<:AbstractFloat}) where {T<:Real}
41+
@inline function result(val::T, der::Real, a::Measurement{<:Real}) where {T<:Real}
4242
newder = empty_der1(a)
4343
@inbounds for tag in keys(a.der)
4444
if ! iszero(tag[2]) # Skip values with 0 uncertainty
@@ -56,9 +56,9 @@ end
5656
# Get the common type parameter of a collection of Measurement objects. The first two
5757
# methods are for the trivial cases of homogeneous tuples and arrays, the last, inefficient,
5858
# method is for inhomogeneous collections (probably the least common case).
59-
gettype(::Tuple{Measurement{T}, Vararg{Measurement{T}}}) where {T<:AbstractFloat} = T
60-
gettype(::AbstractArray{Measurement{T}}) where {T<:AbstractFloat} = T
61-
_eltype(::Measurement{T}) where {T<:AbstractFloat} = T
59+
gettype(::Tuple{Measurement{T}, Vararg{Measurement{T}}}) where {T<:Real} = T
60+
gettype(::AbstractArray{Measurement{T}}) where {T<:Real} = T
61+
_eltype(::Measurement{T}) where {T<:Real} = T
6262
gettype(collection) = promote_type(_eltype.(collection)...)
6363

6464
# This function is similar to the previous one, but applies to mathematical
@@ -249,7 +249,7 @@ function Base.:/(a::Measurement, b::Measurement)
249249
return result(x / y, (oneovery, -x * abs2(oneovery)), (a, b))
250250
end
251251
Base.:/(a::Real, b::Measurement) = result(a/b.val, -a/abs2(b.val), b)
252-
Base.:/(a::Measurement{T}, b::Real) where {T<:AbstractFloat} = result(a.val/b, 1/T(b), a)
252+
Base.:/(a::Measurement{T}, b::Real) where {T<:Real} = result(a.val/b, 1/T(b), a)
253253

254254
# 0.0 as partial derivative for both arguments of "div", "fld", "cld" should be
255255
# correct for most cases. This has been tested against "@uncertain" macro.
@@ -289,7 +289,7 @@ function Base.:^(a::Measurement, b::Integer)
289289
return result(x ^ b, b * x ^ (b - 1), a)
290290
end
291291

292-
function Base.:^(a::Measurement{T}, b::Rational) where {T<:AbstractFloat}
292+
function Base.:^(a::Measurement{T}, b::Rational) where {T<:Real}
293293
x = a.val
294294
return result(x ^ b, b * x ^ (b - one(T)), a)
295295
end
@@ -306,7 +306,7 @@ function Base.:^(a::Real, b::Measurement)
306306
return result(res, res*log(a), b)
307307
end
308308

309-
function Base.exp2(a::Measurement{T}) where {T<:AbstractFloat}
309+
function Base.exp2(a::Measurement{T}) where {T<:Real}
310310
pow = exp2(a.val)
311311
return result(pow, pow*log(T(2)), a)
312312
end
@@ -411,47 +411,47 @@ end
411411
# Inverse trig functions: acos, acosd, acosh, asin, asind, asinh, atan, atand, atanh,
412412
# asec, acsc, acot, asech, acsch, acoth
413413

414-
function Base.acos(a::Measurement{T}) where {T<:AbstractFloat}
414+
function Base.acos(a::Measurement{T}) where {T<:Real}
415415
aval = a.val
416416
return result(acos(aval), -inv(sqrt(one(T) - abs2(aval))), a)
417417
end
418418

419-
function Base.acosd(a::Measurement{T}) where {T<:AbstractFloat}
419+
function Base.acosd(a::Measurement{T}) where {T<:Real}
420420
aval = a.val
421421
return result(acosd(aval), -rad2deg(inv(sqrt(one(T) - abs2(aval)))), a)
422422
end
423423

424-
function Base.acosh(a::Measurement{T}) where {T<:AbstractFloat}
424+
function Base.acosh(a::Measurement{T}) where {T<:Real}
425425
aval = a.val
426426
return result(acosh(aval), inv(sqrt(abs2(aval) - one(T))), a)
427427
end
428428

429-
function Base.asin(a::Measurement{T}) where {T<:AbstractFloat}
429+
function Base.asin(a::Measurement{T}) where {T<:Real}
430430
aval = a.val
431431
return result(asin(aval), inv(sqrt(one(T) - abs2(aval))), a)
432432
end
433433

434-
function Base.asind(a::Measurement{T}) where {T<:AbstractFloat}
434+
function Base.asind(a::Measurement{T}) where {T<:Real}
435435
aval = a.val
436436
return result(asind(aval), rad2deg(inv(sqrt(one(T) - abs2(aval)))), a)
437437
end
438438

439-
function Base.asinh(a::Measurement{T}) where {T<:AbstractFloat}
439+
function Base.asinh(a::Measurement{T}) where {T<:Real}
440440
aval = a.val
441441
return result(asinh(aval), inv(hypot(aval, one(T))), a)
442442
end
443443

444-
function Base.atan(a::Measurement{T}) where {T<:AbstractFloat}
444+
function Base.atan(a::Measurement{T}) where {T<:Real}
445445
aval = a.val
446446
return result(atan(aval), inv(abs2(aval) + one(T)), a)
447447
end
448448

449-
function Base.atand(a::Measurement{T}) where {T<:AbstractFloat}
449+
function Base.atand(a::Measurement{T}) where {T<:Real}
450450
aval = a.val
451451
return result(atand(aval), rad2deg(inv(abs2(aval) + one(T))), a)
452452
end
453453

454-
function Base.atanh(a::Measurement{T}) where {T<:AbstractFloat}
454+
function Base.atanh(a::Measurement{T}) where {T<:Real}
455455
aval = a.val
456456
return result(atanh(aval), inv(one(T) - abs2(aval)), a)
457457
end
@@ -572,7 +572,7 @@ function Base.expm1(a::Measurement)
572572
return result(expm1(aval), exp(aval), a)
573573
end
574574

575-
function Base.exp10(a::Measurement{T}) where {T<:AbstractFloat}
575+
function Base.exp10(a::Measurement{T}) where {T<:Real}
576576
val = exp10(a.val)
577577
return result(val, log(T(10))*val, a)
578578
end
@@ -582,7 +582,7 @@ function Base.frexp(a::Measurement)
582582
return (result(x, inv(exp2(y)), a), y)
583583
end
584584

585-
Base.ldexp(a::Measurement{T}, e::Integer) where {T<:AbstractFloat} =
585+
Base.ldexp(a::Measurement{T}, e::Integer) where {T<:Real} =
586586
result(ldexp(a.val, e), ldexp(one(T), e), a)
587587

588588
# Logarithms
@@ -600,17 +600,17 @@ function Base.log(a::Measurement) # Special case
600600
return result(log(aval), inv(aval), a)
601601
end
602602

603-
function Base.log2(a::Measurement{T}) where {T<:AbstractFloat} # Special case
603+
function Base.log2(a::Measurement{T}) where {T<:Real} # Special case
604604
x = a.val
605605
return result(log2(x), inv(log(T(2)) * x), a)
606606
end
607607

608-
function Base.log10(a::Measurement{T}) where {T<:AbstractFloat} # Special case
608+
function Base.log10(a::Measurement{T}) where {T<:Real} # Special case
609609
aval = a.val
610610
return result(log10(aval), inv(log(T(10)) * aval), a)
611611
end
612612

613-
function Base.log1p(a::Measurement{T}) where {T<:AbstractFloat} # Special case
613+
function Base.log1p(a::Measurement{T}) where {T<:Real} # Special case
614614
aval = a.val
615615
return result(log1p(aval), inv(aval + one(T)), a)
616616
end
@@ -719,18 +719,18 @@ Base.rem2pi(a::Measurement, r::RoundingMode) = result(rem2pi(a.val, r), 1, a)
719719

720720
### Machine precision
721721

722-
Base.eps(::Type{Measurement{T}}) where {T<:AbstractFloat} = eps(T)
722+
Base.eps(::Type{Measurement{T}}) where {T<:Real} = eps(T)
723723
Base.eps(a::Measurement) = eps(a.val)
724724

725725
Base.nextfloat(a::Measurement) = result(nextfloat(a.val), 1, a)
726726
Base.nextfloat(a::Measurement, n::Integer) = result(nextfloat(a.val, n), 1, a)
727727

728-
Base.maxintfloat(::Type{Measurement{T}}) where {T<:AbstractFloat} = maxintfloat(T)
728+
Base.maxintfloat(::Type{Measurement{T}}) where {T<:Real} = maxintfloat(T)
729729

730-
Base.floatmin(::Type{Measurement{T}}) where {T<:AbstractFloat} = floatmin(T) ± zero(T)
731-
Base.floatmax(::Type{Measurement{T}}) where {T<:AbstractFloat} = floatmax(T) ± zero(T)
730+
Base.floatmin(::Type{Measurement{T}}) where {T<:Real} = floatmin(T) ± zero(T)
731+
Base.floatmax(::Type{Measurement{T}}) where {T<:Real} = floatmax(T) ± zero(T)
732732

733-
Base.typemax(::Type{Measurement{T}}) where {T<:AbstractFloat} = typemax(T)
733+
Base.typemax(::Type{Measurement{T}}) where {T<:Real} = typemax(T)
734734

735735
### Rounding
736736

@@ -766,13 +766,13 @@ Base.trunc(::Type{Bool}, x::Measurement) = measurement(trunc(Bool, value(x)))
766766

767767
# Widening
768768

769-
Base.widen(::Type{Measurement{T}}) where {T<:AbstractFloat} = Measurement{widen(T)}
769+
Base.widen(::Type{Measurement{T}}) where {T<:Real} = Measurement{widen(T)}
770770

771771
# To big float
772772

773773
Base.big(::Type{Measurement}) = Measurement{BigFloat}
774-
Base.big(::Type{Measurement{T}}) where {T<:AbstractFloat} = Measurement{BigFloat}
775-
Base.big(x::Measurement{<:AbstractFloat}) = convert(Measurement{BigFloat}, x)
774+
Base.big(::Type{Measurement{T}}) where {T<:Real} = Measurement{BigFloat}
775+
Base.big(x::Measurement{<:Real}) = convert(Measurement{BigFloat}, x)
776776
Base.big(x::Complex{<:Measurement}) = convert(Complex{Measurement{BigFloat}}, x)
777777

778778
# Sum and prod

src/parsing.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ julia> measurement("-1234e-1")
8383
"""
8484
measurement(str::AbstractString) = parse(Measurement{Float64}, str)
8585

86-
function Base.tryparse(::Type{Measurement{T}}, str::S) where {T<:AbstractFloat, S<:AbstractString}
86+
function Base.tryparse(::Type{Measurement{T}}, str::S) where {T<:Real, S<:AbstractString}
8787
m = match(rxp_error_with_parentheses, str)
8888
if m !== nothing # "123(45)e6"
8989
val_str::S, val_dec, err_str::S, err_dec_str, expn = m.captures
@@ -128,7 +128,7 @@ function Base.tryparse(::Type{Measurement{T}}, str::S) where {T<:AbstractFloat,
128128
return measurement(val, err)
129129
end
130130

131-
function Base.parse(::Type{Measurement{T}}, str::S) where {T<:AbstractFloat, S<:AbstractString}
131+
function Base.parse(::Type{Measurement{T}}, str::S) where {T<:Real, S<:AbstractString}
132132
out = tryparse(Measurement{T}, str)
133133
out === nothing && throw(ArgumentError("cannot parse $(repr(str)) as Measurement{$T}"))
134134
return out

src/show.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#
1414
# This file defines the methods to represent `Measurement` objects in various places.
1515
#
16+
# FIXME: should special-handle non-AbstractFloat T's.
17+
#
1618
### Code:
1719

1820
import Printf

src/special-functions.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,40 @@ using .SpecialFunctions
2121

2222
# Error function: erf, erfinv, erfc, erfcinv, erfcx, erfi, dawson
2323

24-
function SpecialFunctions.erf(a::Measurement{T}) where {T<:AbstractFloat}
24+
function SpecialFunctions.erf(a::Measurement{T}) where {T<:Real}
2525
aval = a.val
2626
return result(erf(aval), 2*exp(-abs2(aval))/sqrt(T(pi)), a)
2727
end
2828

29-
function SpecialFunctions.erfinv(a::Measurement{T}) where {T<:AbstractFloat}
29+
function SpecialFunctions.erfinv(a::Measurement{T}) where {T<:Real}
3030
res = erfinv(a.val)
3131
# For the derivative, see http://mathworld.wolfram.com/InverseErf.html
3232
return result(res, sqrt(T(pi)) * exp(abs2(res)) / 2, a)
3333
end
3434

35-
function SpecialFunctions.erfc(a::Measurement{T}) where {T<:AbstractFloat}
35+
function SpecialFunctions.erfc(a::Measurement{T}) where {T<:Real}
3636
aval = a.val
3737
return result(erfc(aval), -2*exp(-abs2(aval))/sqrt(T(pi)), a)
3838
end
3939

40-
function SpecialFunctions.erfcinv(a::Measurement{T}) where {T<:AbstractFloat}
40+
function SpecialFunctions.erfcinv(a::Measurement{T}) where {T<:Real}
4141
res = erfcinv(a.val)
4242
# For the derivative, see http://mathworld.wolfram.com/InverseErfc.html
4343
return result(res, -sqrt(T(pi)) * exp(abs2(res)) / 2, a)
4444
end
4545

46-
function SpecialFunctions.erfcx(a::Measurement{T}) where {T<:AbstractFloat}
46+
function SpecialFunctions.erfcx(a::Measurement{T}) where {T<:Real}
4747
aval = a.val
4848
res = erfcx(aval)
4949
return result(res, 2 * (aval * res - inv(sqrt(T(pi)))), a)
5050
end
5151

52-
function SpecialFunctions.erfi(a::Measurement{T}) where {T<:AbstractFloat}
52+
function SpecialFunctions.erfi(a::Measurement{T}) where {T<:Real}
5353
aval = a.val
5454
return result(erfi(aval), 2*exp(abs2(aval))/sqrt(T(pi)), a)
5555
end
5656

57-
function SpecialFunctions.dawson(a::Measurement{T}) where {T<:AbstractFloat}
57+
function SpecialFunctions.dawson(a::Measurement{T}) where {T<:Real}
5858
aval = a.val
5959
res = dawson(aval)
6060
return result(res, one(T) - 2 * aval * res, a)

src/utils.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ computed as the standard score between their difference and 0:
2020
2121
stdscore(measure_1 - measure_2, 0)
2222
"""
23-
stdscore(a::Measurement{S}, b::Measurement{T}) where {S<:AbstractFloat,T<:AbstractFloat} =
23+
stdscore(a::Measurement{S}, b::Measurement{T}) where {S<:Real,T<:Real} =
2424
stdscore(a - b, zero(promote_type(S, T)))
2525

2626
# Weighted Average with Inverse-Variance Weighting
@@ -39,7 +39,7 @@ end
3939

4040
# Derivative and Gradient
4141
derivative(a::Measurement{F},
42-
tag::Tuple{T, T, UInt64}) where {F<:AbstractFloat, T<:AbstractFloat} =
42+
tag::Tuple{T, T, UInt64}) where {F<:Real, T<:Real} =
4343
get(a.der, tag, zero(F))
4444

4545
"""
@@ -98,7 +98,7 @@ of an independent `Measurement`, and the value is the absolute value of the
9898
product between its uncertainty and the partial derivative of `x` with respect
9999
to this `Measurement`.
100100
"""
101-
function uncertainty_components(x::Measurement{T}) where {T<:AbstractFloat}
101+
function uncertainty_components(x::Measurement{T}) where {T<:Real}
102102
out = Dict{Tuple{T, T, UInt64}, T}()
103103
for var in keys(x.der)
104104
out[var] = abs(var[2] * Measurements.derivative(x, var))

test/runtests.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ end
1515
isapprox(x::Measurement, y::Measurement; rest...) =
1616
isapprox(x.val, y.val; nans = true, rest...) &&
1717
isapprox(x.err, y.err; nans = true, rest...)
18-
isapprox(x::Complex{Measurement{<:AbstractFloat}},
19-
y::Complex{Measurement{<:AbstractFloat}}; rest...) =
18+
isapprox(x::Complex{Measurement{<:Real}},
19+
y::Complex{Measurement{<:Real}}; rest...) =
2020
isapprox(real(x), real(y); nans = true, rest...) &&
2121
isapprox(imag(x), imag(y); nans = true, rest...)
2222
# This is bit strict, but the idea is that in the tests we want `Measurement`s to be

0 commit comments

Comments
 (0)