Skip to content

Commit 32fbb7d

Browse files
committed
- Removed bugs in FloatmuIterator
- Added method `eligible_step`
1 parent 83dd390 commit 32fbb7d

File tree

6 files changed

+167
-39
lines changed

6 files changed

+167
-39
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MicroFloatingPoints"
22
uuid = "6e0295d3-a2ae-4d9e-ab31-7b55fc0f4333"
33
authors = ["Frédéric Goualard <[email protected]>"]
4-
version = "1.0.0"
4+
version = "1.1.0"
55

66
[deps]
77
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"

docs/src/developer.md

+6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ MicroFloatingPoints.roundfrac(f,szf)
3535
MicroFloatingPoints.float64_to_uint32mu(x::Float64,szE,szf)
3636
```
3737

38+
## Iterators
39+
40+
```@docs
41+
check_eligibility_step(start::Floatmu{szE,szf}, stop::Floatmu{szE,szf}, step::Floatmu{szE,szf}) where {szE,szf}
42+
```
43+
3844
## The `MicroFloatingPoints.MFPRandom` module
3945

4046
```@meta

docs/src/manual.md

+8
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ FloatmuIterator{szE,szf}
215215
FloatmuIterator{2,2}(-1.25, -0.25, 0.25)
216216
```
217217

218+
It is possible to know in advance the number of floats in the resulting range with the [`length`](https://docs.julialang.org/en/v1/base/collections/#Base.length) function.
219+
220+
As stated in the documentation for `FloatmuIterator` above, one cannot use a floating-point step smaller than the largest gap in the domain we iterate through. The function `eligible_step` gives the smallest value allowed when given two bounds.
221+
222+
```@docs
223+
eligible_step
224+
```
225+
218226
### Rounding
219227

220228
We have seen in section [Creating a `Floatmu` float](@ref) that each `Floatmu` retains the information whether the value it was created from required rounding or not.

src/Floatmu.jl

+107-36
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,56 @@ Return the value of the global inexact flag.
11371137
"""
11381138
inexact() = return inexact_flag
11391139

1140+
1141+
"""
1142+
eligible_step(start::Floatmu{szE,szf}, stop::Floatmu{szE,szf}) where {szE,szf}
1143+
eligible_step(::Type{Floatmu{szE,szf}}, start::Float64, stop::Float64) where {szE,szf}
1144+
1145+
Return the smallest `Floatmu{szE,szf}` eligible step allowed to iterate through the domain
1146+
`[start,stop]`.
1147+
1148+
# Examples
1149+
1150+
```jldoctest
1151+
julia> eligible_step(Floatmu{2,2}(-0.5),Floatmu{2,2}(2.5))
1152+
0.5
1153+
```
1154+
"""
1155+
function eligible_step end
1156+
1157+
function eligible_step(start::Floatmu{szE,szf}, stop::Floatmu{szE,szf}) where {szE,szf}
1158+
maxi = max(abs(start),abs(stop))
1159+
return maxi - prevfloat(maxi) # The difference of two consecutive floats is always representable
1160+
end
1161+
1162+
function eligible_step(::Type{Floatmu{szE,szf}}, start::Float64, stop::Float64) where {szE,szf}
1163+
return eligible_step(Floatmu{szE,szf}(start),Floatmu{szE,szf}(stop))
1164+
end
1165+
1166+
1167+
"""
1168+
check_eligibility_step(start::Floatmu{szE,szf}, stop::Floatmu{szE,szf},
1169+
step::Floatmu{szE,szf}) where {szE,szf}
1170+
1171+
Check whether the step `step` can be reliability used for the domain [start, stop].
1172+
Throw an ArgumentError exception in the negative.
1173+
"""
1174+
function check_eligibility_step(start::Floatmu{szE,szf}, stop::Floatmu{szE,szf},
1175+
step::Floatmu{szE,szf}) where {szE,szf}
1176+
if isnan(start) || isnan(stop) || isinf(start) || isinf(stop)
1177+
throw(ArgumentError("bounds of the iteration cannot be NaNs or infinities"))
1178+
end
1179+
if step == 0.0
1180+
throw(ArgumentError("the step cannot be zero"))
1181+
end
1182+
# Finding the largest distance between two floats in the domain
1183+
eligible = eligible_step(start,stop)
1184+
if step < eligible
1185+
throw(ArgumentError("the step cannot be reliably used. Use a step greater or equal to $eligible"))
1186+
end
1187+
end
1188+
1189+
11401190
"""
11411191
FloatmuIterator(start::Floatmu{szE,szf},stop::Floatmu{szE,szf},
11421192
step::Floatmu{szE,szf}) where {szE,szf}
@@ -1155,8 +1205,26 @@ can be initialized with two `Floatmu{szE,szf}` or with two `Float64`.
11551205
One may iterate from one float to the next (the default) or choose some step.
11561206
The step may be a number of floats or an amount to add.
11571207
1158-
An ArgumentError is raised if the bounds are NaNs or if the step chosen is zero
1159-
(or rounds to zero when converted to a `Floatmu{szE,szf}`).
1208+
An ArgumentError is raised if the bounds are NaNs, if the step chosen is zero
1209+
(or rounds to zero when converted to a `Floatmu{szE,szf}`), or if the
1210+
step is a value smaller than the largest distance between two consecutive
1211+
floats in `[last, stop]` (use [`eligible_step`](@ref) to know the smallest
1212+
value allowed).
1213+
1214+
When the step is an amount to add, the bounds cannot be infinities.
1215+
1216+
When the step is a number of floats, infinities are allowed for the bounds and are always
1217+
part of the resulting range:
1218+
```jldoctest
1219+
julia> collect(FloatmuIterator(Floatmu{2,2},-Inf,Inf,5))
1220+
6-element Vector{Floatmu{2, 2}}:
1221+
-Infμ{2, 2}
1222+
-1.75
1223+
-0.5
1224+
0.75
1225+
2.0
1226+
Infμ{2, 2}
1227+
```
11601228
11611229
# Examples
11621230
```jldoctest
@@ -1174,31 +1242,35 @@ julia> L2=[x for x = FloatmuIterator(Floatmu{2, 2}, 0.0, 1.0, 2)]
11741242
1.0
11751243
```
11761244
"""
1177-
struct FloatmuIterator{szE,szf}
1245+
mutable struct FloatmuIterator{szE,szf}
11781246
first::Floatmu{szE,szf}
11791247
last::Floatmu{szE,szf}
11801248
step::Union{Int,Floatmu{szE,szf}}
1249+
stopit::Bool
11811250

11821251
function FloatmuIterator(start::Floatmu{szE,szf},stop::Floatmu{szE,szf},
11831252
step::Floatmu{szE,szf}) where {szE,szf}
1184-
if isnan(start) || isnan(stop)
1185-
throw(ArgumentError("bounds of the iteration cannot be NaNs"))
1186-
end
1187-
if step == 0.0
1188-
throw(ArgumentError("the step shall be non-null"))
1189-
end
1190-
return new{szE,szf}(start,stop,step)
1253+
check_eligibility_step(start,stop,step);
1254+
1255+
return new{szE,szf}(start,stop,step,false)
11911256
end
1257+
11921258
function FloatmuIterator(start::Floatmu{szE,szf},stop::Floatmu{szE,szf},
11931259
step::Float64) where {szE,szf}
1194-
if isnan(start) || isnan(stop)
1195-
throw(ArgumentError("bounds of the iteration cannot be NaNs"))
1196-
end
1197-
if step == 0.0
1198-
throw(ArgumentError("the step shall be non-null"))
1199-
end
1200-
return new{szE,szf}(start,stop,Floatmu{szE,szf}(step))
1260+
mustep = Floatmu{szE,szf}(step)
1261+
check_eligibility_step(start,stop,mustep)
1262+
return new{szE,szf}(start,stop,mustep,false)
1263+
end
1264+
1265+
function FloatmuIterator(::Type{Floatmu{szE,szf}},start::Float64,stop::Float64,
1266+
step::Float64) where {szE,szf}
1267+
mustart = Floatmu{szE,szf}(float64_to_uint32mu(start, szE, szf),nothing)
1268+
mustop = Floatmu{szE,szf}(float64_to_uint32mu(stop, szE, szf),nothing)
1269+
mustep = Floatmu{szE,szf}(step)
1270+
check_eligibility_step(mustart,mustop,mustep)
1271+
return new{szE,szf}(mustart, mustop, mustep, false)
12011272
end
1273+
12021274
function FloatmuIterator(start::Floatmu{szE,szf},stop::Floatmu{szE,szf},
12031275
step::Int = 1) where {szE,szf}
12041276
if isnan(start) || isnan(stop)
@@ -1207,8 +1279,9 @@ struct FloatmuIterator{szE,szf}
12071279
if step == 0
12081280
throw(ArgumentError("the step shall be non-null"))
12091281
end
1210-
return new{szE,szf}(start,stop,step)
1282+
return new{szE,szf}(start,stop,step,false)
12111283
end
1284+
12121285
function FloatmuIterator(::Type{Floatmu{szE,szf}},start::Float64,stop::Float64,
12131286
step::Int = 1) where {szE,szf}
12141287
if isnan(start) || isnan(stop)
@@ -1218,39 +1291,37 @@ struct FloatmuIterator{szE,szf}
12181291
throw(ArgumentError("the step shall be non-null"))
12191292
end
12201293
return new{szE,szf}(Floatmu{szE,szf}(float64_to_uint32mu(start, szE, szf),nothing),
1221-
Floatmu{szE,szf}(float64_to_uint32mu(stop, szE, szf),nothing),step)
1222-
end
1223-
function FloatmuIterator(::Type{Floatmu{szE,szf}},start::Float64,stop::Float64,
1224-
step::Float64) where {szE,szf}
1225-
if isnan(start) || isnan(stop)
1226-
throw(ArgumentError("bounds of the iteration cannot be NaNs"))
1227-
end
1228-
mustep = Floatmu{szE,szf}(step)
1229-
if mustep == 0.0
1230-
throw(ArgumentError("the step shall be non-null"))
1231-
end
1232-
return new{szE,szf}(Floatmu{szE,szf}(float64_to_uint32mu(start, szE, szf),nothing),
1233-
Floatmu{szE,szf}(float64_to_uint32mu(stop, szE, szf),nothing),mustep)
1294+
Floatmu{szE,szf}(float64_to_uint32mu(stop, szE, szf),nothing),step,false)
12341295
end
12351296
end
12361297

12371298
function iterate(iter::FloatmuIterator{szE,szf},state=iter.first) where {szE,szf}
1238-
if state <= iter.last
1299+
if state <= iter.last && !iter.stopit
12391300
if iter.step isa Int
1240-
return (state,nextfloat(state,iter.step))
1301+
nextstate = nextfloat(state,iter.step)
12411302
else
1242-
return (state,state+iter.step)
1303+
nextstate = state+iter.step
12431304
end
1305+
iter.stopit = (state == nextstate)
1306+
return (state,nextstate)
12441307
else
12451308
return nothing
12461309
end
12471310
end
12481311

12491312
function length(iter::FloatmuIterator{szE,szf}) where {szE,szf}
12501313
if iter.step isa Int
1251-
return ceil(Int,nb_fp_numbers(iter.first,iter.last)/iter.step)
1314+
nr = (nb_fp_numbers(iter.first,iter.last)-1)/iter.step
1315+
nt = trunc(nr)
1316+
if isinf(iter.last)
1317+
# If the right bound is an infinity, it will be included in the range even
1318+
# if the number of steps does not match => add 1 value more in the range.
1319+
return Int(nt) + (nt != nr ? 2 : 1)
1320+
else
1321+
return 1 + Int(nt)
1322+
end
12521323
else
1253-
return ceil(Int,(Float64(iter.last)-Float64(iter.first))/Float64(iter.step))
1324+
return 1 + trunc(Int,(Float64(iter.last)-Float64(iter.first))/Float64(iter.step))
12541325
end
12551326
end
12561327

src/MicroFloatingPoints.jl

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export μ, λ, NaNμ, Infμ
2525
export FloatmuIterator
2626
export isinexact, errorsign, reset_inexact, inexact
2727
export Emax, Emin, nb_fp_numbers, bias
28+
export eligible_step
2829

2930
include("Floatmu.jl")
3031

test/iterator.jl

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1-
@testset "FloatmuIterator" begin
2-
@test [x for x in FloatmuIterator(Floatmu{2,2},0.0,1.0)] == [0.0, 0.25, 0.5, 0.75, 1.0]
1+
@testset "FloatmuIterator Floatmu Floatmu Floatmu" begin
2+
@test collect(FloatmuIterator(Floatmu{2,2}(-0.5),Floatmu{2,2}(0.75),Floatmu{2,2}(0.3))) == [-0.5, -0.25, -0.0, 0.25, 0.5, 0.75]
3+
end;
4+
5+
@testset "FloatmuIterator Floatmu Floatmu Float64" begin
6+
@test collect(FloatmuIterator(Floatmu{2,2}(0.0),Floatmu{2,2}(1.75),0.7)) == [0.0, 0.75, 1.5]
7+
@test_throws ArgumentError collect(FloatmuIterator(Floatmu{2,2}(1.0),Floatmu{2,2}(3.5),0.1))
8+
end;
9+
10+
@testset "FloatmuIterator Floatmu Floatmu [Int]" begin
11+
@test collect(FloatmuIterator(Floatmu{2,2}(0.0),Floatmu{2,2}(1.75),4)) == [0.0, 1.0]
12+
end;
13+
14+
@testset "FloatmuIterator Type Float64 Float64 [Int]" begin
15+
@test collect(FloatmuIterator(Floatmu{2,2},0.0,1.0)) == [0.0, 0.25, 0.5, 0.75, 1.0]
16+
@test collect(FloatmuIterator(Floatmu{2,2},1.0,Inf)) == [1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3, 3.5, Infμ(Floatmu{2,2})]
17+
@test collect(FloatmuIterator(Floatmu{2,2},-Inf,-1.0)) == [-Infμ(Floatmu{2,2}), -3.5, -3, -2.5, -2.0, -1.75, -1.5, -1.25, -1.0]
18+
@test collect(FloatmuIterator(Floatmu{2,2},-Inf,Inf,5)) == [-Infμ(Floatmu{2,2}), -1.75, -0.5, 0.75, 2.0, Infμ(Floatmu{2,2})]
19+
end;
20+
21+
@testset "FloatmuIterator Type Float64 Float64 Float64" begin
22+
@test_throws ArgumentError collect(FloatmuIterator(Floatmu{2,2},1.25,3.0,0.25))
23+
end;
24+
25+
@testset "length" begin
26+
@test length(FloatmuIterator(Floatmu{2,2},-0.25,2.5)) == 11
27+
@test length(FloatmuIterator(Floatmu{2,2},-3.0,-0.5)) == 9
28+
@test length(FloatmuIterator(Floatmu{2,2},-Inf,-1.5)) == 7
29+
@test length(FloatmuIterator(Floatmu{2,2},-Inf,Inf)) == 25
30+
@test length(FloatmuIterator(Floatmu{2,2},-Inf,Inf,3)) == 9
31+
@test length(FloatmuIterator(Floatmu{2,2},-3.5,3.5,1.0)) == 8
32+
@test length(FloatmuIterator(Floatmu{2,2},-1.0,1.5,0.25)) == 11
33+
@test length(FloatmuIterator(Floatmu{2,2},-1.0,1.5,0.5)) == 6
34+
@test length(FloatmuIterator(Floatmu{2,2}(0.0),Floatmu{2,2}(1.75),0.7)) == 3
35+
@test length(FloatmuIterator(Floatmu{2,2}(0.0),Floatmu{2,2}(1.75),4)) == 2
36+
end;
37+
38+
@testset "eligible_step" begin
39+
@test eligible_step(Floatmu{2,2}(0.25),Floatmu{2,2}(2.0)) == 0.25
40+
@test eligible_step(Floatmu{2,2}(0.25),Floatmu{2,2}(2.5)) == 0.5
41+
@test eligible_step(Floatmu{8,23}(0.0),Floatmu{8,23}(1.0)) == 2.0^-24
42+
@test eligible_step(Floatmu{8,23}(-1.0),Floatmu{8,23}(0.0)) == 2.0^-24
43+
@test eligible_step(Floatmu{8,23}(-1.0),Floatmu{8,23}(0.5)) == 2.0^-24
44+
@test eligible_step(Floatmu{8,23}(-0.5),Floatmu{8,23}(1.0)) == 2.0^-24
345
end;

0 commit comments

Comments
 (0)