8

I am trying to understand this code:

  r = (1:10) - (4/1)
    println(r)

Output:

-3.0:1.0:6.0

I understood why I got -3 and 6. But why I got that value in the middle (1.0)? How does Julia calculate it? Or how I can google it?

edwardmlyte
  • 15,937
  • 23
  • 58
  • 83
konstantin_doncov
  • 2,725
  • 4
  • 40
  • 100

3 Answers3

12

(first:step:last) syntax represent a Range type in Julia

typeof(1:10) # => UnitRange{Int32}

If step part is omitted, by default it is assumed 1

1:10 == 1:1:10 # => true

A Range is a compact view of a series

collect(1:10) # => 10-element Array{Int32,1}:
#  1
#  2
#  3
#  4
#  5
#  6
#  7
#  8
#  9
# 10

So it's expected that a Range type and a Vector follow the same rules e.g when you add a constant value like this:

collect(1+(1:10))==collect(1:10)+1 # => true

or even adding two vectors give you the same result of adding their range representation like this:

collect((1:10)+(1:10))==collect(1:10)+collect(1:10) # => true
Reza Afzalan
  • 5,646
  • 3
  • 26
  • 44
  • 1
    It is probably just a typo, but it is not true that `1:10 === 1:1:10`, only that `1:10 == 1:1:10`. The first is a `UnitRange` and the second is a `StepRange`. It might also good to mention that `isa(1:10,AbstractVector) # => true`. – Andreas Noack Oct 22 '15 at 13:24
  • Thanks @Andreas for his comment on `1:10 !== 1:1:10` really it was a typo, I edit that. now `1:10 == 1:1:10 # => true` – Reza Afzalan Oct 22 '15 at 13:50
3

The division operator in 4/1 returns a Float64. Although the original Range is a size 1 Int step Range, after adding a floating point to both sides it becomes a Float64 Range. As such, a step size of 1.0 is created by converting the implicit integer step size (floating point numbers are non-uniformly distributed, so uniform stepping is a little tricky - sometimes there are rounding issues).

Dan Getz
  • 17,002
  • 2
  • 23
  • 41
2

You can see this when applying float to an interval:

julia> 1:10
1:10

julia> float(1:10)
1.0:1.0:10.0

and this promotion is required before adding to the Float64 4/1 (4.0).

Similarly, when adding an integer to a float julia "promotes" the integer to a float before adding/subtracting:

julia> 1 + 2.0
3.0

julia> @which 1 + 2.0
+(x::Number, y::Number) at promotion.jl:172

see the promotion rules:

+(x::Number, y::Number) = +(promote(x,y)...)

You can @which follow the function calls all the way down to understand what's going on (all the way to the following):

julia> @which +(1:10, 2.0)
+(A::AbstractArray{T,N}, x::Number) at arraymath.jl

julia> @which .+(1:10, 2.0)
.+(r::Range{T}, x::Real) at range.jl

julia> @which .+(2.0, 1:10)
.+(x::Real, r::UnitRange{T<:Real}) at range.jl

# which is defined as
.+(x::Real, r::UnitRange)  = range(x + r.start, length(r))

and hence promotion-addition of Int64 and Float64.


Note in master the display of interval is slightly less confusing/ambiguous:

julia> float(1:10)
10-element FloatRange{Float64}:
 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0

julia> 1:10
10-element UnitRange{Int64}:
 1,2,3,4,5,6,7,8,9,10
Andy Hayden
  • 359,921
  • 101
  • 625
  • 535