Running the following code in julia REPL

``````x = 1.1; h = 0.1;
x_list = [i for i in (x-2h):h:(x+2h)]
``````

Gives the following output

0.9000000000000001
1.0000000000000002
1.1
1.2000000000000002

I’m expecting the output would be the following

0.9
1.0
1.1
1.2
1.3

``````julia> 1.1 - 2*0.1
0.9000000000000001
``````

This is not Julia-specific, it’s due to the fact that standard floating-point arithmetic doesn’t exactly represent decimal values like `1.1` and `0.1`. For example, in Python 3:

``````>>> 1.1 - 2*0.1
0.9000000000000001
``````

See:

3 Likes

my particular problem is
The output print 4 elements
0.9000000000000001
1.0000000000000002
1.1
1.2000000000000002
I’m expecting 5 elements
0.9
1.0
1.1
1.2
1.3

Thanks

Try

``````collect(LinRange(0.9,1.3,5))
``````
2 Likes

It works, thank you!

This is an intrinsic problem with floating-point ranges, unfortunately — due to roundoff errors perturbing the endpoints, it can change the number of points by 1.

The solution is to explicitly specify the desired length, with `range(x-2h, step=h, length=5)`, or `range(x-2h, x+2h, length=5)`, or `LinRange(x-2h, x+2h, 5)`. (`range` and `LinRange` compute their elements in slightly different ways.)

9 Likes

Or `range(0.9, 1.3, 5)`. You should also consider, @Allan_Olave, whether to just drop the `collect` and leave it as a range object.

3 Likes

Another option is using `StepRangeLen` directly with the `offset` argument. This should be more precise than `LinRange`. (For `x = 1.1`, `h = 0.1`, it produces the same values as `LinRange`. But in other cases, it might not.)

``````julia> StepRangeLen(x, h, 5, 3) |> collect
5-element Vector{Float64}:
0.9000000000000001
1.0
1.1
1.2000000000000002
1.3

julia> LinRange(x-2h, x+2h, 5) |> collect
5-element Vector{Float64}:
0.9000000000000001
1.0
1.1
1.2000000000000002
1.3
``````

`StepRangeLen` also preserves the exact `step` while `LinRange` does not:

``````julia> StepRangeLen(x, h, 5, 3) |> step
0.1

julia> LinRange(x-2h, x+2h, 5) |> step
0.09999999999999998
``````
2 Likes

But as far as I know, this is what `range` does, and that would be the ‘user interface’ for `StepRangeLen`, instead of calling the constructor directly.

`range` creates a `StepRangeLen` (in this case), but without using `offset`. This forces you to use `x-2h` or `x+2h` as the reference value, with possible loss of precision:

``````julia> range(x-2h, step=h, length=5) |> collect
5-element Vector{Float64}:
0.9000000000000001
1.0000000000000002
1.1
1.2000000000000002
1.3000000000000003

julia> StepRangeLen(x, h, 5, 3) |> collect
5-element Vector{Float64}:
0.9000000000000001
1.0
1.1
1.2000000000000002
1.3
``````

(Note that the second and fifth elements are different.)

2 Likes

This is close in spirit to your original formulation but does the range with integers to ensure five elements. And you don’t need much knowledge about Julia’s range types.

``````julia> x = 1.1; h = 0.1;

julia> xlist = x .+ (-2:1:2)*h
0.9000000000000001:0.1:1.3

julia> collect(xlist)
5-element Vector{Float64}:
0.9000000000000001
1.0
1.1
1.2000000000000002
1.3
``````
6 Likes

Try this

``````range(x-2h, x+2h, 5)
``````

This results in the same values in this specific case (`x=1.1`, `h=0.1`), but can still be less precise for other values:

``````julia> x = 0.7777952957598123; h = 0.9328657194781406;

julia> x ∈ range(x-2h, x+2h, 5) # shouldn’t this range contain x?
false

julia> x ∈ StepRangeLen(x, h, 5, 3)
true

julia> range(x-2h, x+2h, 5) |> collect
5-element Vector{Float64}:
-1.087936143196469
-0.15507042371832835
0.7777952957598122   # this isn’t the original x …
1.7106610152379527
2.6435267347160933

julia> StepRangeLen(x,h,5,3) |> collect
5-element Vector{Float64}:
-1.087936143196469
-0.15507042371832824
0.7777952957598123   # … but this is!
1.7106610152379529
2.6435267347160933
``````
1 Like

That is strange, because I thought this was exactly the sort of thing `range` was designed to do correctly.

I would argue it is doing it correctly, it’s as precise as it can be with the information it is given.

`range(x-2h, x+2h, 5)` doesn’t know about the original `x` and `h`, it only sees the results of the computations `x-2h` and `x+2h`, both of which are not exact (due to floating-point arithmetic). Therefore, `range(x-2h, x+2h, 5)` does not necessarily create a range with step `h`, or one that contains `x`.

Of course, this loss of precision may not be important, so using `range` is perfectly fine. I just wanted to mention `StepRangeLen` with `offset` as probably the most precise option for a given `x` and `h`.

2 Likes

`x .+ (-2:2)*h` contains x exactly, another advantage.

4 Likes