@@ -70,6 +70,7 @@ Valid invocations of range are:
70
70
* Call `range` with one of `stop` or `length`. `start` and `step` will be assumed to be one.
71
71
72
72
See Extended Help for additional details on the returned type.
73
+ See also [`logrange`](@ref) for logarithmically spaced points.
73
74
74
75
# Examples
75
76
```jldoctest
@@ -252,10 +253,13 @@ end
252
253
# # 1-dimensional ranges ##
253
254
254
255
"""
255
- AbstractRange{T}
256
+ AbstractRange{T} <: AbstractVector{T}
256
257
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`.
259
263
"""
260
264
abstract type AbstractRange{T} <: AbstractArray{T,1} end
261
265
@@ -550,6 +554,8 @@ julia> collect(LinRange(-0.1, 0.3, 5))
550
554
0.19999999999999998
551
555
0.3
552
556
```
557
+
558
+ See also [`Logrange`](@ref Base.LogRange) for logarithmically spaced points.
553
559
"""
554
560
struct LinRange{T,L<: Integer } <: AbstractRange{T}
555
561
start:: T
@@ -620,7 +626,7 @@ parameters `pre` and `post` characters for each printed row,
620
626
`sep` separator string between printed elements,
621
627
`hdots` string for the horizontal ellipsis.
622
628
"""
623
- function print_range (io:: IO , r:: AbstractRange ,
629
+ function print_range (io:: IO , r:: AbstractArray ,
624
630
pre:: AbstractString = " " ,
625
631
sep:: AbstractString = " , " ,
626
632
post:: AbstractString = " " ,
@@ -1488,3 +1494,179 @@ julia> mod(3, 0:2) # mod(3, 3)
1488
1494
"""
1489
1495
mod (i:: Integer , r:: OneTo ) = mod1 (i, last (r))
1490
1496
mod (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