@@ -70,6 +70,7 @@ Valid invocations of range are:
7070* Call `range` with one of `stop` or `length`. `start` and `step` will be assumed to be one.
7171
7272See Extended Help for additional details on the returned type.
73+ See also [`logrange`](@ref) for logarithmically spaced points.
7374
7475# Examples
7576```jldoctest
@@ -252,10 +253,13 @@ end
252253# # 1-dimensional ranges ##
253254
254255"""
255- AbstractRange{T}
256+ AbstractRange{T} <: AbstractVector{T}
256257
257- Supertype for ranges with elements of type `T`.
258- [`UnitRange`](@ref) and other types are subtypes of this.
258+ Supertype for linear ranges with elements of type `T`.
259+ [`UnitRange`](@ref), [`LinRange`](@ref) and other types are subtypes of this.
260+
261+ All subtypes must define [`step`](@ref).
262+ Thus [`LogRange`](@ref Base.LogRange) is not a subtype of `AbstractRange`.
259263"""
260264abstract type AbstractRange{T} <: AbstractArray{T,1} end
261265
@@ -550,6 +554,8 @@ julia> collect(LinRange(-0.1, 0.3, 5))
550554 0.19999999999999998
551555 0.3
552556```
557+
558+ See also [`Logrange`](@ref Base.LogRange) for logarithmically spaced points.
553559"""
554560struct LinRange{T,L<: Integer } <: AbstractRange{T}
555561 start:: T
@@ -620,7 +626,7 @@ parameters `pre` and `post` characters for each printed row,
620626`sep` separator string between printed elements,
621627`hdots` string for the horizontal ellipsis.
622628"""
623- function print_range (io:: IO , r:: AbstractRange ,
629+ function print_range (io:: IO , r:: AbstractArray ,
624630 pre:: AbstractString = " " ,
625631 sep:: AbstractString = " , " ,
626632 post:: AbstractString = " " ,
@@ -1488,3 +1494,179 @@ julia> mod(3, 0:2) # mod(3, 3)
14881494"""
14891495mod (i:: Integer , r:: OneTo ) = mod1 (i, last (r))
14901496mod (i:: Integer , r:: AbstractUnitRange{<:Integer} ) = mod (i- first (r), length (r)) + first (r)
1497+
1498+
1499+ """
1500+ logrange(start, stop, length)
1501+ logrange(start, stop; length)
1502+
1503+ Construct a specialized array whose elements are spaced logarithmically
1504+ between the given endpoints. That is, the ratio of successive elements is
1505+ a constant, calculated from the length.
1506+
1507+ This is similar to `geomspace` in Python. Unlike `PowerRange` in Mathematica,
1508+ you specify the number of elements not the ratio.
1509+ Unlike `logspace` in Python and Matlab, the `start` and `stop` arguments are
1510+ always the first and last elements of the result, not powers applied to some base.
1511+
1512+ # Examples
1513+ ```jldoctest
1514+ julia> logrange(10, 4000, length=3)
1515+ 3-element Base.LogRange{Float64, Base.TwicePrecision{Float64}}:
1516+ 10.0, 200.0, 4000.0
1517+
1518+ julia> ans[2] ≈ sqrt(10 * 4000) # middle element is the geometric mean
1519+ true
1520+
1521+ julia> range(10, 40, length=3)[2] ≈ (10 + 40)/2 # arithmetic mean
1522+ true
1523+
1524+ julia> logrange(1f0, 32f0, 11)
1525+ 11-element Base.LogRange{Float32, Float64}:
1526+ 1.0, 1.41421, 2.0, 2.82843, 4.0, 5.65685, 8.0, 11.3137, 16.0, 22.6274, 32.0
1527+
1528+ julia> logrange(1, 1000, length=4) ≈ 10 .^ (0:3)
1529+ true
1530+ ```
1531+
1532+ See the [`LogRange`](@ref Base.LogRange) type for further details.
1533+
1534+ See also [`range`](@ref) for linearly spaced points.
1535+
1536+ !!! compat "Julia 1.11"
1537+ This function requires at least Julia 1.11.
1538+ """
1539+ logrange (start:: Real , stop:: Real , length:: Integer ) = LogRange (start, stop, Int (length))
1540+ logrange (start:: Real , stop:: Real ; length:: Integer ) = logrange (start, stop, length)
1541+
1542+
1543+ """
1544+ LogRange{T}(start, stop, len) <: AbstractVector{T}
1545+
1546+ A range whose elements are spaced logarithmically between `start` and `stop`,
1547+ with spacing controlled by `len`. Returned by [`logrange`](@ref).
1548+
1549+ Like [`LinRange`](@ref), the first and last elements will be exactly those
1550+ provided, but intermediate values may have small floating-point errors.
1551+ These are calculated using the logs of the endpoints, which are
1552+ stored on construction, often in higher precision than `T`.
1553+
1554+ # Examples
1555+ ```jldoctest
1556+ julia> logrange(1, 4, length=5)
1557+ 5-element Base.LogRange{Float64, Base.TwicePrecision{Float64}}:
1558+ 1.0, 1.41421, 2.0, 2.82843, 4.0
1559+
1560+ julia> Base.LogRange{Float16}(1, 4, 5)
1561+ 5-element Base.LogRange{Float16, Float64}:
1562+ 1.0, 1.414, 2.0, 2.828, 4.0
1563+
1564+ julia> logrange(1e-310, 1e-300, 11)[1:2:end]
1565+ 6-element Vector{Float64}:
1566+ 1.0e-310
1567+ 9.999999999999974e-309
1568+ 9.999999999999981e-307
1569+ 9.999999999999988e-305
1570+ 9.999999999999994e-303
1571+ 1.0e-300
1572+
1573+ julia> prevfloat(1e-308, 5) == ans[2]
1574+ true
1575+ ```
1576+
1577+ Note that integer eltype `T` is not allowed.
1578+ Use for instance `round.(Int, xs)`, or explicit powers of some integer base:
1579+
1580+ ```jldoctest
1581+ julia> xs = logrange(1, 512, 4)
1582+ 4-element Base.LogRange{Float64, Base.TwicePrecision{Float64}}:
1583+ 1.0, 8.0, 64.0, 512.0
1584+
1585+ julia> 2 .^ (0:3:9) |> println
1586+ [1, 8, 64, 512]
1587+ ```
1588+
1589+ !!! compat "Julia 1.11"
1590+ This type requires at least Julia 1.11.
1591+ """
1592+ struct LogRange{T<: Real ,X} <: AbstractArray{T,1}
1593+ start:: T
1594+ stop:: T
1595+ len:: Int
1596+ extra:: Tuple{X,X}
1597+ function LogRange {T} (start:: T , stop:: T , len:: Int ) where {T<: Real }
1598+ if T <: Integer
1599+ # LogRange{Int}(1, 512, 4) produces InexactError: Int64(7.999999999999998)
1600+ throw (ArgumentError (" LogRange{T} does not support integer types" ))
1601+ end
1602+ if iszero (start) || iszero (stop)
1603+ throw (DomainError ((start, stop),
1604+ " LogRange cannot start or stop at zero" ))
1605+ elseif start < 0 || stop < 0
1606+ # log would throw, but _log_twice64_unchecked does not
1607+ throw (DomainError ((start, stop),
1608+ " LogRange does not accept negative numbers" ))
1609+ elseif ! isfinite (start) || ! isfinite (stop)
1610+ throw (DomainError ((start, stop),
1611+ " LogRange is only defined for finite start & stop" ))
1612+ elseif len < 0
1613+ throw (ArgumentError (LazyString (
1614+ " LogRange(" , start, " , " , stop, " , " , len, " ): can't have negative length" )))
1615+ elseif len == 1 && start != stop
1616+ throw (ArgumentError (LazyString (
1617+ " LogRange(" , start, " , " , stop, " , " , len, " ): endpoints differ, while length is 1" )))
1618+ end
1619+ ex = _logrange_extra (start, stop, len)
1620+ new {T,typeof(ex[1])} (start, stop, len, ex)
1621+ end
1622+ end
1623+
1624+ function LogRange {T} (start:: Real , stop:: Real , len:: Integer ) where {T}
1625+ LogRange {T} (convert (T, start), convert (T, stop), convert (Int, len))
1626+ end
1627+ function LogRange (start:: Real , stop:: Real , len:: Integer )
1628+ T = float (promote_type (typeof (start), typeof (stop)))
1629+ LogRange {T} (convert (T, start), convert (T, stop), convert (Int, len))
1630+ end
1631+
1632+ size (r:: LogRange ) = (r. len,)
1633+ length (r:: LogRange ) = r. len
1634+
1635+ first (r:: LogRange ) = r. start
1636+ last (r:: LogRange ) = r. stop
1637+
1638+ function _logrange_extra (a:: Real , b:: Real , len:: Int )
1639+ loga = log (1.0 * a) # widen to at least Float64
1640+ logb = log (1.0 * b)
1641+ (loga/ (len- 1 ), logb/ (len- 1 ))
1642+ end
1643+ function _logrange_extra (a:: Float64 , b:: Float64 , len:: Int )
1644+ loga = _log_twice64_unchecked (a)
1645+ logb = _log_twice64_unchecked (b)
1646+ # The reason not to do linear interpolation on log(a)..log(b) in `getindex` is
1647+ # that division of TwicePrecision is quite slow, so do it once on construction:
1648+ (loga/ (len- 1 ), logb/ (len- 1 ))
1649+ end
1650+
1651+ function getindex (r:: LogRange{T} , i:: Int ) where {T}
1652+ @inline
1653+ @boundscheck checkbounds (r, i)
1654+ i == 1 && return r. start
1655+ i == r. len && return r. stop
1656+ # Main path uses Math.exp_impl for TwicePrecision, but is not perfectly
1657+ # accurate, hence the special cases for endpoints above.
1658+ logx = (r. len- i) * r. extra[1 ] + (i- 1 ) * r. extra[2 ]
1659+ x = _exp_allowing_twice64 (logx)
1660+ return T (x)
1661+ end
1662+
1663+ function show (io:: IO , r:: LogRange{T} ) where {T}
1664+ print (io, " LogRange{" , T, " }(" )
1665+ ioc = IOContext (io, :typeinfo => T)
1666+ show (ioc, first (r))
1667+ print (io, " , " )
1668+ show (ioc, last (r))
1669+ print (io, " , " )
1670+ show (io, length (r))
1671+ print (io, ' )' )
1672+ end
0 commit comments