News for hackers. Stuff that matters.
Re:Again on 0-based vs. 1-based indexing by chmod775 on Wednesday January 20, 2021 @12:47AM (#25842948)
Here's two arguments arguments in favor of 0-based 'indexing' (or offsets), that aren't just "because that's how it's done":

1. It is faster. Due to how memory works, 1-based languages need to subtract 1 internally every time you access an array[1].

2. It works mathematically better with some of the most common operations on array offsets, like modulo and division into round/ceil/floor. You'll be peppering your code with +1 or -1 around those a lot if you use 1-based indexing.

[1]: This is lua, for instance: https://github.com/lua/lua/blob/master/ltable.c#L702

Re:Again on 0-based vs. 1-based indexing by anand-bala on Wednesday January 20, 2021 @01:01AM (#25843031)
(On the phone so forgive the possible typos)

I am not sure (1) is a very convincing argument. The subtraction of 1 internally is not necessarily gonna be there once the code gets compiled.

I will personally concede with you on (2), but it's still not very convincing considering that Fortran (a 1-index language) is still (arguably) the most popular language for linear algebra.

Personally, I feel "The Index Wars" are the same as the "Text Editor Wars": a matter for personal opinion.

Re:Again on 0-based vs. 1-based indexing by chmod775 on Wednesday January 20, 2021 @01:39AM (#25843218)
>I am not sure (1) is a very convincing argument. The subtraction of 1 internally is not necessarily gonna be there once the code gets compiled.

Theoretically one can optimize it - though only when one can statically infer the index and the compiler decides to inline the array access function. If you can't do that, the best you can hope for is a fast CPU instruction like LEA or something.

In theory one could make a lot of things fast, in practice they rarely are, and it's always better to avoid problems now than to pray later.

Re:Again on 0-based vs. 1-based indexing by stabbles on Wednesday January 20, 2021 @10:49AM (#25847815)
Oh come one now, don't be silly.

Think about vectorization and loop unrolling. It _always_ does a memory load with an offset as a single CPU instruction.

As an example a 4 times unrolled sum of doubles on AVX looks like this:

    L64:
     vaddpd ymm0, ymm0, ymmword ptr [rcx + 8*rsi]
     vaddpd ymm1, ymm1, ymmword ptr [rcx + 8*rsi + 32]
     vaddpd ymm2, ymm2, ymmword ptr [rcx + 8*rsi + 64]
     vaddpd ymm3, ymm3, ymmword ptr [rcx + 8*rsi + 96]
     add rsi, 16
     cmp rdx, rsi
     jne L64
The `rcx + 8*rsi + 32` stuff is offsets the compiler generates. Don't even think about worrying about -1 here...
Re:Again on 0-based vs. 1-based indexing by chmod775 on Wednesday January 20, 2021 @06:31PM (#25853312)
> Oh come one now, don't be silly.

Don't be snarky.

I mentioned it's theoretically possible and outlined the specific conditions for optimizations to happen.

But I also mentioned that in reality various things often prevent compilers (assuming it's a compiled language, and not interpreted, like that LUA implementation) from applying such optimizations:

- you're programming against an API or are compiling an API (i.e. non-static functions).

- you have time-constraints for emitting optimized code, like most JITs - you can't afford any deep analysis that would enable such optimizations, you're mostly pattern-matching for lower hanging fruits.

Since pictures say more than a thousand words, here's the actual disassembly of that lua function I linked earlier, as shipped by my Linux distribution: https://i.imgur.com/DnqVC8E.png

The green stuff is the "fast path". For 0-based indexing you would completely drop the first LEA and replace those last MOV/SHL/LEA with a (faster?) MOV r,r/SHL/ADD r,m - which is what GCC is likely to generate.

So that's Lua (one implementation at least) in practice. No compiler magic making arrays fast here.

LuaJIT (from my current reading) goes the different route of just pretending the first array element doesn't exist, meaning that in memory they have an element '0', but they simply don't use it. This trades some memory for speed.

From lj_tab.c:

   ** The array size is non-inclusive. E.g. asize=128 creates array slots            
   ** for 0..127, but not for 128. If you need slots 1..128, pass asize=129          
   ** (slot 0 is wasted in this case).
Personally I quite like this approach. A lot better than hoping compilers will magically fix everything and quite hard to argue with, considering LuaJIT's performance. Though it would be a ludicrous design for a lower-level language.
Re:Again on 0-based vs. 1-based indexing by segfaultbuserr on Wednesday January 20, 2021 @04:20AM (#25844310)
> It works mathematically better with some of the most common operations on array offsets

The problem is that for every formula or calculation best suited for 1-based indexing, there is another which is better implemented using 0-based indexing. Regardless of which indexing mode you are using, peppering your code with +1 or -1 is eventually inevitable. It's why I think the choice is a matter of preference and has little to do with technical reasons.

Overall I still prefer 0-based indexing - a few bytes of RAM is cheap nowadays, when the calculation can be simplified using 1-based indexing, deliberately not using [0] and pretending that the array begins at [1] is often an acceptable option (the trick was originally invented for porting Fortran code...)

Re:Again on 0-based vs. 1-based indexing by user-the-name on Wednesday January 20, 2021 @05:18AM (#25844650)
Not convinced that is the case. I do not pepper my code with +1 and -1 while using 0-based indexing.

I can think of a lot of cases where I would do it for 1-based indexing, but very few for 0-based indexing. If you want to make the claim that there similarly many, you're going to have to come up with some substantial examples.

Re:Again on 0-based vs. 1-based indexing by tsegers on Wednesday January 20, 2021 @06:59AM (#25845359)
The -1 with 0-based indexing occurs very frequently when using the amount of elements in some collection to derive boundaries of the indexes, which will range from 0 up to and including n-1. Grepping for "- 1" in my code folder returns mostly expressions that look like 'length/len/size - 1'.
Re:Again on 0-based vs. 1-based indexing by user-the-name on Wednesday January 20, 2021 @07:06AM (#25845426)
This can happen, but it is fairly rare, especially if you favour handling ranges as either (start, length) tuples, or as half-open intervals, with the end index being one past the highest index. This representation simplifies many things, many of them unrelated to 0- or 1-based indexing.
Re:Again on 0-based vs. 1-based indexing by tsegers on Wednesday January 20, 2021 @07:17AM (#25845536)
While that is true, ranges and intervals are not native constructs in quite a few languages.
Re:Again on 0-based vs. 1-based indexing by ufo on Wednesday January 20, 2021 @01:02PM (#25849622)
One of the more common examples is if you need to iterate backwards. It's awkward because the [0,N) interval becomes (N,0].

for(i=N-1; i>=0; i--)

Re:Again on 0-based vs. 1-based indexing by naniwaduni on Wednesday January 20, 2021 @02:46PM (#25851028)
Notably, this requires you to use a signed index type, which is absurd.

The alternative is to use the so-called goes-to operator: for (i=N; i --> 0;). But this actually relies on the postincrement semantics, which is a huge wart.

Re:Again on 0-based vs. 1-based indexing by IlliOnato on Wednesday January 20, 2021 @01:58AM (#25843320)
Your (1) only affects compilation time, and even this is assuming a particular implementation of arrays (which is not a given).

Your (2) I don't fully understand. Why would you need to use "modulo and division into round/ceil/floor" on array offsets? How often you do it?

For what it's worth, I've written a _ton_ of code in an obscure language called Omnimark, which is 1-based. Much of that code involved using arrays. I've just grepped through a large Omnimark codebase I helped to write, and no, it is _not_ peppered with "+1" or "-1".

Re:Again on 0-based vs. 1-based indexing by adwn on Wednesday January 20, 2021 @04:28AM (#25844368)
> Your (1) only affects compilation time, [...]

No, it mostly affects execution time. Whether the effect is meaningful, however, depends on various factors.

> [...] and even this is assuming a particular implementation of arrays (which is not a given).

Eventually, all arrays boil down to pointer+offset memory accesses. Well, if your programming language internally implements "arrays" as hashmaps, then you've already lost performance-wise, of course.

> Why would you need to use "modulo and division into round/ceil/floor" on array offsets?

Whenever you're working with 2D/3D data in manually managed memory.

> How often you do it?

When you're writing web services? Probably not so much. When you're doing image manipulation or 3D graphics? All the time.

Re:Again on 0-based vs. 1-based indexing by 3JPLW on Wednesday January 20, 2021 @09:23AM (#25846742)
> No, it mostly affects execution time.

Citation needed. Yes, the -1 appears in the source code. It might even appear in the intermediate representation. It'll even show up in the assembly for multidimensional arrays (but it'll all get folded into a single `mov` assembly for vectors).

But I dare you to actually demonstrate a performance difference. Compared to a memory access, a `dec` is free.

Re:Again on 0-based vs. 1-based indexing by professoretc on Wednesday January 20, 2021 @02:09PM (#25850537)
> No, it mostly affects execution time.

That's what I was thinking, too, but most of the time (even if the index is not static) the -1 can be eliminated by just modifying the address at which the array is assumed to start. That is,

    arr[x]
compiles into an access at address

    (arr - 1) + x
Since the address of arr is statically known, arr - 1 is, too, and the address of "one before the first element" will be what's written as the base for the memory access.
Re:Again on 0-based vs. 1-based indexing by adwn on Wednesday January 20, 2021 @03:06PM (#25851262)
> Since the address of arr is statically known [...]

No, that's almost never the case – only when accessing a static array. Typically, that restricts it to static look-up tables and very simple embedded systems.

Re:Again on 0-based vs. 1-based indexing by nvader on Wednesday January 20, 2021 @11:12AM (#25848136)
At some point I would like to write up a story about this team in my University Object-Oriented Programming class who decided to use 1-based 1ndexes in Java while making a 2-D array for a board game.

I don't want to spoil it, but the following picture of a how they stored a 3x3 tic-tac-toe board is a hint:

  [ null, null, null, null, null, null,

  null, X, 0, X, null, null,

  null, null, O, X, X, null, 

  null, null, null, X, 0, 0 ]
Re:Again on 0-based vs. 1-based indexing by xxs on Wednesday January 20, 2021 @07:19AM (#25845551)
>When you're writing web services? Probably not so much.

I bet it's done a lot as a lot of data structures are implemented vid pow2 length arrays. It's just the people who write them are unaware.

Re:Again on 0-based vs. 1-based indexing by foxrob92 on Wednesday January 20, 2021 @03:59AM (#25844161)
> Why would you need to use "modulo and division into round/ceil/floor" on array offsets? How often you do it?

Circular buffers?

Re:Again on 0-based vs. 1-based indexing by tijsvd on Wednesday January 20, 2021 @06:09AM (#25844991)
Nah, it doesn't matter if you do (prev + 1) % len, or (prev % len) + 1.

Hash tables matter more though.

Re:Again on 0-based vs. 1-based indexing by xxs on Wednesday January 20, 2021 @07:08AM (#25845445)
>% len,

Modulus (division) is slow and the length should be pow2 and the operation "& (len-1)". If you do %len, you have far greater issues. I have pretty extensive experience writing hashmap/cyclic buffers and the like. If you have auto-grow structs (and you almost always want that), you want pow2 length arrays. e.g.

  addLast(e)
    elements[tail++] = e;
    tail &= elements.length - 1;
    if (tail == head) doubleCapacity();
  }
Re:Again on 0-based vs. 1-based indexing by naniwaduni on Wednesday January 20, 2021 @08:19AM (#25846060)
This is entirely orthogonal to whether you do "prev+1 & len-1" or "(prev & len-1) + 1". (In fact, the latter gives a more natural construction if you fill downward.)
Re:Again on 0-based vs. 1-based indexing by xxs on Wednesday January 20, 2021 @12:07PM (#25848875)
Adding/advancing is indeed easier. What about going backwards (insert before head or remove from tail), i.e when 0, then length. One way to do it is something like that, assume 2 compliment and there is negative shift left available. Not straightforward:

  int h = ( -(--head) >>> 31) ^ 1;//if head was equal to 1 (and only 1), h = 1, otherwise zero    
  head += h * len; //or shift left, if there is log2 len available; still, mul is a fast operation, unlike div
Of course, it can implemented via branching but that would be a major downside for 1-based idx.
Re:Again on 0-based vs. 1-based indexing by xxs on Wednesday January 20, 2021 @05:40AM (#25844807)
>on array offsets? How often you do it?

Every =single= hash map worth its salt should be power of 2 length backed array. And the hashing function (for the idx of the array) is not the division (that's slow and cannot be parallelized in the cpu) but a simple bitwise AND.

In short offset 1 doesn't map well to hardware.

Re:Again on 0-based vs. 1-based indexing by IncRnd on Wednesday January 20, 2021 @03:57AM (#25844154)

  1) It also affects runtime, when addresses need to have a subtraction, and maybe a multiplication, in the offset calculation.
  2) You may need this often in certain network or protocol code, for example.
Re:Again on 0-based vs. 1-based indexing by cbolton on Wednesday January 20, 2021 @07:58AM (#25845874)
Regarding the mathematical advantage: it depends on the subfield, but I think 1-based numbering is more common in mathematical notation and algorithms (linear algebra being a prominent example).

Regarding performance: some mentioned that the 1 offset can often be optimized away by the compiler. But even when this is not possible, common CPU architectures include instructions that can apply the offset at no extra cost, see https://stackoverflow.com/questions/28032685/is-there-a-perf... , so 1-based indexing is just as fast.

Re:Again on 0-based vs. 1-based indexing by elcomet on Wednesday January 20, 2021 @03:31AM (#25843968)
For 2), in Julia, there is "mod1" function which acts as modulo, with 1 being the smallest value instead of zero. It doesn't really make sense mathematically, but is very useful for looping over circular arrays.
Re:Again on 0-based vs. 1-based indexing by im3w1l on Wednesday January 20, 2021 @05:19AM (#25844653)
EDIT: This is actually wrong, because it doesn't correctly describe how negative arguments are handled.

It can be phrased mathematically like this:

Consider the equivalence relation a ≡ b (mod n). It has n equivalence classes. Use 1..n as the canonical elements for the classes.

mod1 takes a number, finds its equivalence class and returns the canonical element.

Re:Again on 0-based vs. 1-based indexing by user-the-name on Wednesday January 20, 2021 @05:20AM (#25844660)
Does it have it for any other reason than to work around the limitations of 1-based indexing?
Re:Again on 0-based vs. 1-based indexing by naniwaduni on Wednesday January 20, 2021 @02:49PM (#25851069)
12-hour timekeeping, perhaps ;-)
Re:Again on 0-based vs. 1-based indexing by nyanpasu64 on Wednesday January 20, 2021 @04:22AM (#25844328)
A while back, I wrote a blog post elaborating on point 2 and inclusive/exclusive indexing and slicing, but it didn't get any traction on HN: https://nyanpasu64.github.io/blog/the-gridline-mental-model-...
Re:Again on 0-based vs. 1-based indexing by jbverschoor on Wednesday January 20, 2021 @02:08AM (#25843397)
Only one reason exists these days: it prevents bugs because people mix up order, infecting, counting, arrays semantics, implementation, pointer arithmetic