People argue that zero-based is incidental, and that 1-based is the right way because of it's long history in mathematics notation. I would argue that 1-based is incidental, and that zero-based is better most of the time for modern math and computer architectures.
Whatever your answer, I suspect it is more cognitive overhead to remember than "always 1 based" or "always 0 based".
Depends what the library function does of course. If it's shape preserving (e.g. if it's a map over the array), it'll generally preserve the axes of the input array.
> What if I do an outer product of two arrays with different base indices?
You'll get an offset array indexed by the product of the axes of the input:
julia> A = OffsetArray(1:3, 0:2)
OffsetArray(::UnitRange{Int64}, 0:2) with eltype Int64 with indices 0:2:
1
2
3
julia> B = 4:6
4:6
julia> A*B'
OffsetArray(::Array{Int64,2}, 0:2, 1:3) with eltype Int64 with indices 0:2×1:3:
4 5 6
8 10 12
12 15 18
> Whatever your answer, I suspect it is more cognitive overhead to remember than "always 1 based" or "always 0 based".Sure, but the real point here is that in most situations, you don't actually care what the axes are, because you use the higher level abstractions (e.g. iteration over elements or the index space, linear algebra operations, broadcasts, etc.), which all know how to deal with arbitrary axes. The only time you should ever really have to think about what your axes are is if there's some practical relevance to your problem (OffsetArrays are used heavily in the images stack).
This points to another example where 0-based should be preferred. When doing modulo arithmetic, 0..N-1 mod N gives 0..N-1, but 1..N mod N puts the zero at the end. I also cringe at languages where -1 mod N does not equal N-1.
For any language with a mod operator, a mod b should always be equal to (a + k×b) mod b for any integer k.
Breaking this invariant makes the mod operator useless in pretty much every application I ever have for it. In e.g. JavaScript I need to define a silly helper function like
mod = (x,y) => x%y + y*(x%y && x>0^y>0)
And then remember to never use the native % operator.The other (“remainder”) version which takes its sign from the first argument is pretty much worthless in practice. IMO it doesn’t need any name at all. But what it definitely doesn’t need is a shorter and more convenient name than the useful modulo operator. Its ubiquity is a serious screwup in programming language design, albeit largely accidental.
In my opinion neither are used sufficiently often to justify taking up a valuable ASCII symbol (same with xor).
I don’t know what is available as chip instructions, I could certainly believe hardware designers made the wrong choice.
[0] http://www.lispworks.com/documentation/lw50/CLHS/Body/f_floo...
[1] http://www.lispworks.com/documentation/lw50/CLHS/Body/f_mod_...
As one simple example, it is frequently useful to map floats into the range [0, 2π), but I have never once wanted to map positive floats into the range [0, 2π) while negative floats get mapped into (–2π, 0] by the same code.
- https://github.com/JuliaLang/julia/issues/14826
mod(-eps(), 4.0)
- https://github.com/JuliaLang/julia/issues/17415 fld(1.0,0.2)
- https://github.com/JuliaLang/julia/issues/3127 mod(1.0,0.1)
- https://github.com/JuliaLang/julia/issues/3104 mod(0.95,0.01)
I'm pretty sure there are more issues.re: mod 2pi, typically it is most accurate to reduce to (-pi,pi) (i.e. division rounding to nearest). Also to get it accurate you need fancy range reduction algorithms, hence julia has rem2pi https://docs.julialang.org/en/stable/base/math/#Base.Math.re...
These examples don’t really have much bearing on the general usefulness of “remainder” vs. “modulo” though.
That's precisely what it does!
> I also cringe at languages where -1 mod N does not equal N-1.
julia> mod(-1,10)
9
But admittedly when you use 1-indexed arrays, any kind of modulo operator becomes pretty inconvenient a lot of the time (lots of futzing to avoid off-by-1 or boundary errors). So maybe it doesn’t matter in the Julia context.