701 comments

[ 3.2 ms ] story [ 353 ms ] thread
I have always assumed that is just one less operation required to resolve the absolute memory address.
No.

1. Optimizing compilers exist.

2. Addressing at fixed offsets is cheap (a single instruction): https://en.wikipedia.org/wiki/Addressing_mode

0-based indexing is superior (as a default) because it simplifies a lot of common math, as discussed in TFA.

3. Less buggy for small systems since the Base Pointer address points directly to a record.

4. Macro expansion is often used to store indexes and other 'magic numbers' in Assembler, and this pattern originated either when Assembly was the primary programming language, or even earlier when humans directly punched out cards with machine instructions. Compilers in any remote sense of the luxury we have today did not exist or were not common.

That is true for vectors, but not matrices, right?

  char at(char matrix[10][10], char i, char j) {
    return matrix[i][j];
  }
still has more computation on 1-indexed than on 0-indexed, I believe.
Well you've either got:

memory_address + array_width*i + j

or

memory_address + array_width*(i-1) + (j-1) = (memory_address-array_width-1)+ array_width*i + j

so you can just absorb the extra computation into the pointer.

> 0-based indexing is superior (as a default) because it simplifies a lot of common math, as discussed in TFA.

If that was true, Fortran, Julia, R and Matlab would use 0 instead of 1-based.

The mental model of being an offset to a memory address is, I think, part of it.

I'd be surprised if the use of zero vs one actually made any difference, from a number of operations point of view -- from the compiler's point of view, the first element in the array is the first element in the array, no matter what we call it.

I mean in an extreme edge case maybe if you are computing an index, and it happens to be zero, then in your math maybe some identity related to zero could be exploited (go to element simple_integer*complicated_function(), where simple_number might be zero) but that seems a bit silly.

How would it not make a difference? If you calculate an index at runtime, to get access to the element, in 0 based would be pointer + index. In 1 based it is however pointer + index - 1 clearly there is an extra subtraction there?

In x86 you could probably hide it in the addressing but that does not mean it does not to be computed

If you’re calculating indexes at runtime, you’re probably not worried about that level of optimization.
One option, at least, would be for malloc and friends to return pointer-1, rather than pointer. That said, I'm sure a compilers expert could come up with a much better option.
This is primarily the actual answer. While most languages force you to see the array as an array, in C/C++ (in C++ I'm only referring to the memory allocated variable type, not an "array class") you can see it as a pointer and do your own math to address any part of it that you want. And the calculation for that memory address is idx * sizeof(the thing in your array). So the real answer here is that array semantics needed to match memory address semantics in the languages that provide that.
There's no reason why memory addresses had to start at 0.
(comment deleted)
Pascal's arrays start at 1, at least by default.
Pretty much all variants of BASIC have arrays starting at 1.
I think a language needs to decide if it is "high level" or if it is instead a convenience language on top of assembly.

BASIC and Pascal I am sure would have been considered a high-level language in their day and naturally would have arrays be 1-based.

C on the other hand....

Pascal is a variant of ALGOL-60, made in part because Wirth thought ALGOL-68 was way too complicated. BASIC instead comes from the FORTRAN line.
Except VB.NET which is only 0-based but you can just shut your eyes and imagine it's 1-based because you declare an array by specifying its upper bound which equals its number of elements in 1-based.
This is false. Pascal arrays start at whatever you want. As for what is happening behind closed doors (aka linker), those start at zero, but with an offset kept separately (that's why C blew out of the water Pascal in terms of speed when looping through an array). And in Pascal you cannot create a dynamic array with anything other than 0-based.

You are confusing arrays with strings. Those used to start at 1, but since Unicode took over (~2007) strings are implemented as zero-based too (even though you can still think of them as 1-based when writing code).

Also in Delphi, when creating a cross-platform application, strings are treated as 0-based, with even multiple locations in documentation stressing this importance due to having enough differences between classic VCL and the new kid on the block (aka FireMonkey).

Arrays start at 0 because they're really just pointers. "[x]" is just shorthand for "+ x * sizeof". The first element is the one stored at the pointer address (0 sizeofs ahead of the pointer, the next element is stored 1 sizeof ahead of the pointer, etc.
That answers something like "what are the technical benefits to zero-based arrays" but not "why are zero-based arrays such a strong convention in programming languages." For that you'd have to read the article I guess.
Or it could be:

"technical benefits to zero-based arrays" -> "why zero-based arrays are a strong convention"

Article mentions that Hoye says as much but is dismissive of it. I am not sure why he would be so quick to dismiss.

In C, "a[x]" is effectively syntactic sugar for just "*(a+x)" (conversion rules for "+" means you don't need the sizeof). It also means you can reverse it and write e.g. "5[somearray]" if you really want to make developers want to throw rocks at you.
hehe, that was me :) when I was young.
That's just convention though. There's no reason "[x]" couldn't have been defined to be "+ (x - 1) * sizeof". There would be no runtime cost to this, and the compile time penalty would be tiny even on ancient systems.
Is `(x - 1)` not a runtime cost if `x` is a runtime variable?
Not on many instruction architectures - addressing modes often support adding/subtracting a constant.
I found it interesting that the author phrased their critique so definitely, i.e. "it's NOT this and NOT that", yet concluded the article by saying their whole argument was speculative. That said, I do agree with their premise that it's going to be pretty tough to get a definitive answer as to why 0-indexing won out. And their criticism of others being so ready to declare results is apt, if not a little ironic in this case :)
> yet concluded the article by saying their whole argument was speculative

It's possible to have a speculative argument and also refute other arguments that claim certainty. Pointing out another answer as being wrong does not mean one needs to know the correct answer or claim to have a precise answer.

There is definitely a bunch of math that works out better in 0 based indices. I saw this a lot when I was working on filesystem focused stuff.

I feel like I've also seen algorithms that work out better with 1 based indices, but not as many.

> The usual arguments involving pointer arithmetic

Yeah, I think this exactly why it is zero based. The index became a simple multiplier for sizeof(int).

An assembler spillover into C.
Was C ever much more than a veneer on top of assembler?

Don't get me wrong, it's why I like C.

> Was C ever much more than a veneer on top of assembler?

Maybe in the very early days, but that necessarily had to end as soon as compilers started doing non-trivial optimizations. Today, C is very far from being "portable assembly".

I'd claim intuitively that having all bits 0 is a proper starting point and more efficient if there were any constraints on address, which was likely the case historically.

I'm happy to hear any dissenting opinions if this is inaccurate.

It really comes down to a choice between a machine-focused (0) or human-focused (1) approach.

The 0 makes a lot of sense in a C pointer world where memcpy and other alike functions can be written very thight.

The 1 makes a lot of sense in a human world, when we count, we start at 1, we talk about the "1st", counting on finger starts with 1, etc.

I once were at a Lua (1 indexed language) conference where this was discussed, and Luis started explaining why Lua was 1-index with this sentence: "The 1st argument ...." :)

I don't think 1-based arrays are better notation, even completely ignoring that code has to run on a machine.

99% of the times, the correct approach is to use iterators. When you really need indices (and you almost never do), 0 is more practical, because it matches the "including start, not including end" convention.

> when we count, we start at 1, we talk about the "1st"

Although often with an implicit zero. Under typical North American culture, your 1st birthday, for example, is more accurately the first anniversary of your birthday. Your birth is zero indexed.

No, birthdays are 1 indexed. Your Nth birthday is the day you turn N years old. When you're 1 year old, you've been alive for 1 year, not 2 years.
The moment you are born you are 0 years old (or perhaps 0.75 years old, but we don't usually recognize that). We count from zero in this case, at least implicitly.

In some cultures you are considered 1 the moment you are born, so the zero indexing isn't universal here, but typical in North America as noted earlier.

No, we don't count from 0. That's like saying we start counting a baker's cup from 0 because you can have half a cup.
If we (speaking as a North American) count from 1, does that mean other cultures (e.g. Korean) count from 2?
No, it just means we have a different notion of what a year is in regards to age. Similar to how different cultures can use different units of measurement for length, mass, etc.
Yes, exactly. One of which carries an implicit zero indexing. The date of birth doesn't disappear just because you decided to use a different measuring device.
No, it doesn’t carry an implicit 0 index. Measuring age (in the West) is like measuring distance. You start at 0, but that doesn’t mean the first item is at index 0.
If you took a standard ruler, a measurer of distance and which has literal index marks painted on it, and placed items along it, the item found at the head of the ruler would be found at the 0th index. You're quite right that we think of birth and its anniversaries in the same way.
Birthdays are literally indexed by the moment of birth as zero.

You can't possibly be serious.

What do you do for a living, and why are they paying you not to understand this?
Do you know the definition of countable? A set S is countable if there is a one-to-one mapping from S to N where N is the natural numbers. Do you know that 0 is not a member of the natural numbers? We literally start counting at 1 by definition of countable.
Nope.

Is the empty set countable? (Yes.)

Dictionary:

nat·u·ral num·bers

  the positive integers (whole numbers) 1, 2, 3, etc., and sometimes zero as well

Countable: https://en.wikipedia.org/wiki/Countable_set

Set theory:

https://en.wikipedia.org/wiki/Ordinal_number

From your own link on countable sets:

> Equivalently, a set S is countable if there exists an injective function f : S → N from S to N; it simply means that every element in S corresponds to a different element in N.

Defining N is usually done via a successor set, on which case 0 makes no sense to include.

A successor set is the set of successors of... 0 or 1, depending on what you are doing.
(comment deleted)
An empty set is countable; it has an empty mapping to the natural numbers. Its cardinality is zero.
(comment deleted)
And its ordinalitiy is also 0.

Standard construction of ordinals is that each ordinal is the set of all its predecessors. (0 has no predecessors , hence 0 is the empty set.) (And so finite ordinals have the same ordinaliity as cardinality).

Show me a mathematical text where ‘ordinality’ is defined.
Are you a time traveller from the late 17th century?
Can't they just .. disagree?

Birthdays are clearly 1 indexed, the first birthday is indexed with 1, and not with 0.

They day you're born is birthday number 0
Exactly.

Birthday[0] gives you an out of range exception, since there is no birthday called 0th. Birthdays are [first,second,..] indexed from 1: brithday[1] = first, birthday[2] = second, and so on. That's what indexing a series from 1 means.

The counting activity doesn't begin when the first item is registered; it begins when the counter is initialized to zero. A decision is made to begin counting, along with the realization that nothing has been counting yet. That's when counting has started. When the first item is seen, the counting is then continuing.

Suppose your job is to count some events. You check in for work at 8:00 a.m., but the first event has not registered until noon. By your logic you should not be paid for four hours, because you're paid to count, and counting started at 1.

No, the mathematical definition of counting (i.e. whether or not a set is countable) involves mapping to the natural numbers, which doesn’t include 0.
I don't think your definition is complete. We can count a set by mapping its elements to the natural numbers, and then identifying that number which is highest. However, we must have a provision for identifying zero as the highest when the set and mapping are empty.
What counter? You're saying a counter exists before people start counting? Is this a form of mathematical Platonism?
What does your stopwatch say right now?

Your pedometer?

Your traffic clicker?

Those are devices, not the spoken language.
So if the counter doesn't speak, there is no counting?
A counter can be lazily instantiated just before the first item is counted.
Are you sure?

We are talking about the he numbering, not the work-doing.

You start waiting at 8, and you wait between events. But you only count (increment) when an event happens.

The 0 comes for free when you are ready to count (hello golang and C++, as well as human intuition).

When you count stars in the sky you don't say "0, 1, 2".

You say "..., 1, 2", or if no stars show up, you say "there are 0" after timer expires.

> But you only count (increment) when an event happens.

Increment what?

> When you count stars in the sky you don't say "0, 1, 2".

No, you say, "I'm going to start counting stars now. Okay, 1, 2, ...".

The preparation part is the zero. You counted stars before and reached some number; you're not starting at that number.

You're still thinking like a computer. Most people think of counting in terms of 'here are some apples, how many exactly?'

If you just look at an empty space, the # of apples is equivalent is equivalent to the # of dinosaurs, but they're only equivalent by their absence.

Ok, use the baker and cup as an example. If you have an empty cup and put half of a cup of flour in it, you now have 0.5 cups of flour. Notice the zero before the ".5". That is us, normal humans, realizing that until you add enough to have 1 of something, you have between 0 and 0.999 repeating.
When I'm baking I start by zeroing the scale, then adding enough flour to reach 500 g (or whatever the recipe calls for). That's counting from zero.
I start with a empty cup, then I fill it up to 1 cup.

I then put it in an initially empty bowl

Yes this is correct!

The milliliters in a baker's cup are a scale based on 0!

Typical counting of things starts from 0. If you count apples you implicitly start at zero and add 1 for each apple. If you count age, you start at birth (0) and count years; one for each birthday. That isn't zero based. The difference is the index of the item between the starting point and the next item. In zero based this item is number 0, in one based, this item is number 1. The first year of life is generally considered the year following birth.
>If you count age, you start at birth (0) and count years; one for each birthday.

nitpick: unless you were born on Feb 29.

I didn't know that. Do you get to be 1 year old if you are born on Feb 29, to account for leap years?
You're just 1/4 of the age of everyone else born the same year. You have the same number of laps around the sun as those people, but you've definitely had fewer birthdays. It's a common joke for leap year babies.
I just dealt with this problem(how to store periodic events(including birthdays)). It is a surprisingly difficult problem. After reading up on postgres interval types my latest attempt uses them to store events, where a per year event(like a birthday) is stored as "2 months 15 days". it turns out the postgres project has put quite a bit of thought into making interval types work as expected with regards to months, not an easy task when you consider how difficult it is to treat dates mathematically.

The nice part is that now February 29 "just works" the downside is the impedance between how months and days are numbered and a how an offset from the epoch(beginning of the year) is defined. January 3rd(1-3) is stored as "0 months 2 days" as when it hits, 0 months have passed and 2 days have passed.

So the specific case of February 29 hits when 1 month has passed and 28 days have passed. 3 years out of 4 this will be the same as "2 months 0 days"(3-1) but every forth year this will be(2-28). As an aside, and the specific reason I went with interval types, every event past feburary 29 works just fine with or without a leapyear, that is, the extra day in the middle does not mess up the offset to days after it.

Honestly I curse a little as I wish months and days were 0-based. At least clocks get this right, almost, 12 hour clocks are a special breed of stupid. start at 12, then go to 1 and proceed up to 11. 24 hour clocks properly start at 0. The worst part about 12 hour clocks is that it is almost correct, replace the 12 with a 0 and it every thing be the same but now it makes sense from a moduler math point of view.

A special curse is reserved when I think about how there is no year zero. https://www.postgresql.org/docs/13/functions-datetime.html#F...

> If you count apples you implicitly start at zero and add 1 for each apple.

If that's the case, then how did the ancient Greeks or Romans count before zero was an acceptable concept in their counting system?

By describing it in other ways, I imagine: "How many apples do you have?" "I don't have any."
This is a philosophically deep point which took me a long time to grasp.

There is a narrowing from "nothing at all" to "zero apples" which doesn't happen 'in the world' but is a necessary precondition to counting apples. The existence of any apple is a requirement for there to be zero of them before you put anything in the basket.

So how old is a not-yet-1-year old?
N days or N months depending on how old the baby is.
And it turns out that 0 days, 0 months is 0 years.
And before an hour has even passed, we'd probably say "minutes old" or perhaps "not even an hour/day old", all to avoid saying "zero days/months/years old". But some might still say zero days old, and they'd be both understood and correct (at least logically if not stylistically). That's the "implicit zero" everybody is talking about. We avoid saying it, but that's just a convention of communcation. Logically it's there. You're zero days old before you're one day old.
Elapsed time timers start at 0. Time is continuous. The elapsed time being “out of the womb”, that we call “age”, starts near 0. Five minutes after birth, the baby has been in the world for, or it’s age is, five minutes. If someone asked how old a new baby is you could hear “3 weeks”.

Another definition might be from conception. But birth being “one year old” is illogical. The sperm didn’t even exist yet, one year before birth.

Your mistake is using years - use milliseconds (or nanoseconds) when you wish to express a duration. Years don't even have the same duration/length (leap years, and leap seconds)
Your birth day is the day you are born.

Your first birthday is the first anniversary of your birth day.

We celebrate the anniversaries of our birth day.

Yep, clearer in Spanish. They say cumpleaños which literally means completed year.
No, old calendar systems are one indexed. In those system, there is literally no year zero; the first year is year one. This leads to crazy things like year 100 being part of the "first century" and year 101 being part of the "second century".

That is not the case with age or birthdays which are, thankfully, zero indexed. The first year of human life is age=0, birthdays=0.

It's not "crazy". No one care about which century X00 is. The term "century" doesn't have enough sig figs. X00 is "the turn of the century".
If you don't care then sure, it's not crazy, but if you did care then believe me, it is crazy. Crazy enough that astronomers[0] and software engineers[1] rebelled against the historian's practice and renamed the years preceding 1 AD in the proleptic gregorian calendar year 0, year -1, year -2, et cetera. A major benefit of this is it allows the leap year pattern to stay consistent and the rule to remain legibile for all years, going back before 1 AD. It's also nice because it lets us say "the 90's were the last ten years of the 20th century" and be correct.

0. https://en.wikipedia.org/wiki/Astronomical_year_numbering

1. https://docs.oracle.com/javase/8/docs/api/java/time/temporal...

(comment deleted)
So that we don't create more falsehoods programmers believe about dates, ISO has defined decades and centuries as starting with 0, not 1.

So as far as your job is concerned, that's when they start. Hope that helps.

Haha, this thing is messed up. Your "first birthday" is technically second, because the first was at your, well, day of birth. But we people love to complicate things and count 1st birthday as a number of annual celebration events after the first mm-dd of birth. Off by one as it is.
Yeah age is confusing even for non-computer-folks :)

If you have 4 classes in school today, you would never talk about the 1st class as number 0, the last one is the 4th, not the 3rd.

(comment deleted)
Although, there is a fifth state in your example: When you are not in class. Which is different to an empty set that implies nothingness. When it comes to age, 0 being birth works well because there is truly is nothingness (from your perspective) before birth. When counting from 1 there is suggestion that there are variables that aren't worth speaking of because they are obvious.
Assuming there is nothingness for the fetus the entire nine months in the womb. For that matter, I can't recall being younger than four, so it's all nothingness before then.
Not really. Perspective isn't scientific and often cultural. Koreans, for example, famously have a different take and use a different counting systems to accommodate.

I am assuming that the reader is biased towards the average HN user. That won't always work for everyone who will come across my comment, but close enough for an unpaid contribution. I'm not about to write a novel to make sure I catch every edge case.

It's fundamentally a distinction between end-index and start-index.

Most human counting uses end-index. I.e. "1" is after 1-thing (has passed, is physically obtained, etc.).

Most computer counting uses start-index + length, for efficiency and to better generalize. I.e. "1" is at the memory address immediately before the "1"st item.

Which ultimately creates the "0 index is 1st thing" linguistic confusion.

PS: Also, language predates computers by a few years, and the concept of zero is hard.

good take!

i think a more accurate and complete idea is that humans refer to a thing in its entirety, with things being lined up and scanned in order as only a potential convenience

if you ask someone to identify an object, they'll point to the middle (or center of the most important component) of the object, not to the 'start' or 'end' of it in their field of vision..

that said, the human perspective is subtle and convenient in completely different ways to how a computer manages memory, and this 'end-index' idea seems like a useful way to map the human whole-object perspective to a linear memory-index perspective

They may point to the middle, but that's not a reference to half an object. ;)

The assumption is that the thing is its entirety, as you said.

Hypothetically, I imagine an array index reference, in human terms, would be communicated as "this thing starts here" or "this is the beginning of this thing."

Which isn't a concept or phrasing we have much occasion to use, other than for routes or long length-measured objects?

Birthdays are anniversaries. Anniversaries are annual activities when we celebrate/honor past events. They do not include the event itself. The first anniversary is 1 year after the initial event.
> Birthdays are anniversaries.

Indeed they are, which is why I literally said so in the previous comment. Did you forget to finish reading it?

> They do not include the event itself.

That is true because the event itself is implied information. There is no value in speaking of it. If you stand before us, we can be certain that you had a day of birth (your 0th anniversary). We don't necessarily know how many times you've gone around the sun following that, however, so that is where we find value in communicating additional information.

If you were counting apples, there is the state where you have no apples (index 0), the state where you have one apple (index 1), the state where you have two apples (index 2), etc. When counting you don't need to worry about index 0 because the no apple state is naturally implied. It only becomes interesting and worthy of communication when you have at least one apple to speak of, thus you start at 1. The state found at index 0 is still implicitly there, though.

If I care about apples (for my lunch, or my store), I care about the difference between having 0 of them and not having bothered to count yet.

0!=null

If no apples is the unusual state, like you expected to find an apple in your lunchbox but someone ate it without your knowledge, then certainly there would be reason to communicate the no apples state. It is ignored in communication when it does not provide useful information, but it is not forgotten. The 0th index is implicitly there.
Come to the infinity store - we literally sell everything! (some items may be out of stock)
Yes, the infinite state is also implicitly found within the state set. Conveniently found at the infinity index.
Yes, that’s the explanation for how we count them as we do, but it has no greater weight (and I’d argue less weight) as to why than saying whether a[0] or a[1] should be the first element in an array.

I say it has less weight as birthday is a compound word, the root words of which suggest that your first birthday could logically be the day of your birth rather than a year after it.

My first weddingday was not a year after I got married. My first graduationday was not a year after I graduated.

Ok then everyone must do it like us, right?

There's no culture where you're born at 1 year old and turn 2 on New Years when everyone gets older?

You sure?

That's why I prefer the Superior(TM) Korean age counting. You are one year old when you're born (it's your first year!). You are two year old on the next New Year's day. (Congratulations, it's your second your now!)

So, if you're born on December 31st, you're two years old the next day. (I see no problem, but apparently some people are hung up on such minor details. I can't fathom why.)

Doesn't Korea use the same new year as China? Usually second new moon after winter solstice.
This system is used for racehorses as well.
> You are one year old when you're born (it's your first year!)

That makes no sense to me. It's also your first century. Does that make you one century old? Of course not! The moment you're born, you're not even one hour old, let alone one day.

They must like rounding up. I guess it depends how they refer to it in their language. If it's "he's in his 1st year", that's a big difference from, "he's 1 year old". I'm wondering if the parent comment simply doesn't know Korean or only knows it somewhat to misconstrue the culture behind this.

It could be though that Korea simply never encountered the Mayan or Arabic civilizations as most did encounter one of those two in history, who are famously known to have independently discovered the concept of zero.

Birth of an array can only logically be defined as index zero, the same as a child. The concept of zero was not universal globally and Korea is pretty isolated.

Except that we actually do use this system for years. Thus 1 BC (the first year BC) was followed by 1 AD (the first year AD). Also for centuries, as in 'the 20th century' being the years 1901 to 2000.
There's nothing wrong with the definition per se, but there's the question of what purpose you're putting it to. We should expect more similarity from two "newly 2" children with the "western" system than the Korean one.
> I see no problem, but apparently some people are hung up on such minor details. I can't fathom why.

Americans and their alcohol laws…

A more sensible English translation from Korean would be to use the phrase "in year X" rather than the phrase "X years old": a newborn is in year 1; after 12 months they are in year 2; etc.

In fact, this whole discussion is more about a choice of phrasing rather than the numbers. When indexing arrays, sometimes we're talking about an offset from the first element (starting with "0"), and sometimes we're talking about ordinal element numbers (starting with "first"). Some programming language designers found that offsets are more useful (because that choice tends to simplify the underlying arithmetic), while others found that ordinal numbers are more useful (because the word "first" should mean "1", to simplify communication between people).

Right. I try to refer to a[n] as 'element number n' rather than 'the nth element'.
Oddly(?) though, most parents of young children don't refer to a baby as being "zero years old." Rather, we break them down into smaller units: two days old, six weeks old, four months old.
It helps that change happens particularly fast at that age, so the difference between newborn and 6 months is worth mentioning. Later on the units go up again and we just say "I'm in my 30s" :P

On the other hand a computer/car/house can also be 10 years old but not 0.

True, a few weeks can mean a few milestones at that age. It's still wild to me how quickly babies/kids develop. Every week brings new abilities, experiences, and emotions that weren't there before.
On the other hand a computer/car/house can also be 10 years old but not 0

Really? When I buy a new PC, to me it will get 1 year old only after a year. Before that it is just NEW. Is that what you meant?

Exactly. What we call "new" is the 0th year. It's the same when you start counting seconds. You say "ok counting starts now", then wait a second and say "one", then wait and say "two". Before you say "one", you don't say zero, but it's implied by you waiting a second after you or someone says "go". That's still 0-indexed.
Duration only ever departs from 0, it can never arrive there.

That's why zero-based indexing is good!

The first floor in a USA building is not where a European would expect.
No. Your age is 1-indexed. It's a 'birthday' in English and German ('geburstag'); in French it's 'anniversaire', and so on. But pretty much everyone indexes age from 1. The fact of your birth is the transition from (legal) non-existence to existence, the equivalent of a dimensionless point.
Age is definitely 0-indexed - a newborn and exactly 1 year old differ by a year. People also count months during the first year, which is still 0 years old. As in 00:xx is the first hour, and 01:xx is the second. If you're 30 years old, it's your 31st year of life now.

But gregorian epoch itself is 1-based. 1AD (0001-01-01) goes right after 1BC (-0001-12-31). There was no 0000-mm-dd. That's why 3rd "millenium" and 21st century started at 2001-01-01 and not at 2000-01-01. YYYY means not how many whole years already passed, but which incomplete year goes right now. On the other hand, your age means "whole years passed since birth [plus maybe a few months]".

Age is not indexed.

And the birthdays are definitely 1 indexed: you denote your first birthday with 1, and not with 0, and so on. You don't denote any birthdays of yours with 0. You may denote something else with it, but birthdays[0] gives an out of range exception. (Especially true in French, where birthdays are called anniversaire, but in English too.)

Demographers sometimes describe age as “the number of completed years”, which always struck me as a wonderfully simple explanation.
If you think of the index as an offset you would start with 0.

BTW on which level is the 1st floor?

> BTW on which level is the 1st floor?

in Europe or in the US?

Does Europe has a zero floor? Please tell me Europe zero-indexes their stories!
They do, it's called the ground floor.
In France, it is called the rez-de-chaussée. The “premier étage” (literally translated as “first floor”) is what the US calls the second floor.
A particularly topographically challenged building in Paris has exits to two enclosing streets end up on different floors. The elevator is numbered -2, -1, rez-de-rue [exit north], rez-de-chaussée [exit south], 1, 2, ... .
Don't know French, but I wonder if the meaning of "etage" is similar to Polish "piętro", which literaly means something like "elevation". So, basically in Polish we have a specific word for ground level, and then we count how much elevated above the the ground the current level is. That's why "1st floor" is the one "elevated one level above the ground".

In English you count "floors" and floor is a usable, hard surface on which you can put something, like a chair. That's why a floor on the ground level is treated the same as the floor above it - it is equally good on accommodating chairs, beds, and other stuff.

Interesting, thanks!

Etymologically, étage comes from the Greek στέγω (and gave the English word “stage”); it is a typically wooden cover. Since the first floor was often instead a continuation of the outside road (way back!), it was not considered a “stage”.

I've always called floor zero and everybody understands.
Yeah, most or all of Europe does that. In English it is called ground floor, first floor, etc. In Swedish the zero numbering makes total sense becasuse instead of numbering the floors we say ground floor, "1 stair", "2 stairs", etc.
You've got ...P3, P2, P1, G, M, 1, 2, 3...
That depends on your language and culture, "floor" is not easily translated, some languages have a word describing all the layers added to the base layer, so "1. sal" (Danish as an example) actually means "the first layer added on top of the base house".

Again, this is more a spoken language/culture thing, and this goes back to what premises we use to communicate with the machines, ours or the machines...

Nobody knows that one.

No elevator I've seen has yet taken a cue from UI design: simply put the buttons within an outline of the building, along with the local numbering scheme.

No more visits to the serial killer lurking in the basement.

The players in this market evidently have been operating at T'ump levels of intelligence. /s

In Tucson I've seen some buildings where the basement is the first floor. You enter at floor two.
Is it machine-focused that we can convert a distance of 1.5 m to 150 cm just by multiplying by 100?

Maybe distances should be human-focused, so that no displacement is equivalently expressed as 1 cm, 1 m, 1 km, ...

Then converting a distance d from m to cm is (d - 1) x 100 + 1.

... but if you have something that's 150cm, that's not 151cm, that's 150cm?
> It really comes down to a choice between a machine-focused (0) or human-focused (1) approach.

Just think of 0-based as offset-based and 1-based as index-based. Both are intuitive just like that. I never get why people arguing over this bring pointers and memory (or anything computer related) to the table. No normal person is going to understand that, but everyone understands that if you don't move at all (0 offset) you stay at the first (index 1) item. Add one and ... you get the point.

Offset-based lets you refer to an abstract location that is after the last element without resorting to n + 1.

  [ ] [ ] [ ] ...    [ ]
 0   1   2   3   n-1     n

We can regard this n as a "virtual zero", and then make it possible to index the n - 1 element also indexable as just -1. The index -n then aliases to 0.
(comment deleted)
And then the counting niceties come along: those perennial +1 mistakes are caused by the fact that, e.g. 4 and 5 are two numbers but they are only one apart.
"ordinal" is preferred to "index", as "index" doesn't naturally suggest starting at "1", or even restricting the key to integers. "Ordinal" starts from 1, everyone agrees (except mathematicians, of course :-) ).
Ask 100 random people on the street and I'd be surprised if even 1 knew the definition of "ordinal". It's an uncommon word.
Do people not study grammar in American schools? I thought all kids learn the distinction between cardinal (one, two, three) and ordinal (first, second, third) numbers.
The English grammar that Americans study in school is likely somewhat different than the English grammar that is taught outside of America as the goal of the latter is likely focused on helping students map their native language onto English. I would agree that `ordinal` is uncommon word for many Americans, but it wouldn't at all surprise me if there were languages where the equivalent word was far more common, and therefore its use and translation was a part of standard English as a second language curriculum.
> The English grammar that Americans study in school is likely somewhat different than the English grammar that is taught outside of America as the goal of the latter is likely focused on helping students map their native language onto English

English is spoken as a native language in many countries outside of America.

(comment deleted)
At the age that cardinal and ordinal numbers are taught, kids simply don't remember "cardinal" and "ordinal", they remember "one, two, three" and "first, second, third".

99% of the instances I've seen "ordinal" outside of this thread has been in code/documentation. It is not a common word in everyday language.

So is "array". What's your point? My point is that it is unambiguous once you say what you are talking about.
I disagree. The CS definition of array, sure, but if you were to say “we have an array of options to eat”, most people with a high school education would know what you mean.
But I think that still leaves open the question of "offset from what?"

If you move zero from the first element, you're still at the first element... but if you move zero from the fourth element you're still at the fourth element. If you move one from before the first element, you're at the first element.

I think you're more or less right, but I think we still need something to motivate the first element as the point of reference, and the machine focus is one way to do that.

In some languages/environments, the array is literally the same as the pointer to the first element.

Other languages are more sophisticated (like humans are!) and can say that position 0 does not exist. An array of size 0 has no elements. An array of size 5 has elements 1at through 5th. An array of size N has 1st through Nth, inclusive.

"Offset-based" is bringing in pointers; that's the thing it's an offset "from". "The beginning of the array" is just a pointer.

I suppose saying that does have an advantage over explicitly talking about pointers, in that the word "pointer" is a piece of jargon that has a lot of baggage. That's just avoiding jargon, though, not really using a different model.

No, offset means "measuring from origin", pointers use that language they don't provide it.

The advantages in measuring from origin can accrue to the person choosing to do it, because there are other reasons to do so which aren't satisfying the CPU.

No, offset means "measuring from a defined location". In a computer, the choice of location is more or less arbitrary[0]. If you pick the first element, you get zero indexing. If you pick a space before the first element, you get one indexing. Either one is a pointer, since a pointer is just computer jargon for "a defined location".

Calling that defined location "origin" doesn't suddenly make it not a pointer.

[0]- NUMA and cache effects aside, as they don't matter for this purpose

I must say in this case Google does it right: https://cloud.google.com/spanner/docs/reference/standard-sql...

In Google standard SQL, to access an array it is simply not allowed to put a number inside square brackets. You must specify which way you mean. So `SELECT some_numbers[OFFSET(1)], some_numbers[ORDINAL(1)]` is allowed but not `SELECT some_numbers[1]`.

That sounds actually great to avoid any ambiguity. At the cost of a bit of verbosity, though.
that's actually good verbosity, because the intent is very clear using this method, and multiple people can read the code and unambiguously identify the intent without having to talk to the original author.

This type of verbosity makes sense in a big organization where the left hand doesn't talk much to the right hand much, so communication naturally evolves to happen at the code level.

Indeed.

I like for example using enum types to ensure that what’s actually passed is the expected value even though fundamentally the semantics do not need to be much more complicated than integer values. There could be the same thing with a distinction between offset and indices as two different integer numerical types to avoid any ambiguity.

> when we count, we start at 1

That is false; counting begins by initializing an accumulator to zero. When you register the first item, the count jumps from 0 to 1.

Who sets an accumulator to zero and then adds one in everyday language?
For instance, someone who makes a fist with zero extended fingers before counting.

Or someone who simply becomes motivated to count something, without making any utterances or gestures to that effect. The motivation is followed by the persistent awareness that nothing has been counted yet, which then changes to 1.

Not saying "zero" doesn't mean we don't mean it. All counting starts with 0. We just say "one" out loud as the first number. We probably should say "zero" too, but why say a word when you can say no words?
Or we just start counting with 1, since 1 is the first number we start with. Do small kids start with a concept of zero and then adding one to it, or do they just start at one?
If you start at 1, how do you answer the question "how many apples are in the basket", when the basket is empty?

Or do you believe that the answer "none" or "zero" is then given without the activity of counting having taken place?

What do we call the meta-activity then: the procedure that results either in the "empty" answer or "one", "two"? Whatever that activity is called, it starts with a concept of zero. Let's call that activity "quanting". Quanting starts with a motivation to enumerate items, and an initially empty result. When no items are present, quanting terminates, reporting that zero/nothing/none result. Otherwise quanting branches into a subprocedure called counting, and that begins at 1.

Humans could do basic counting prior to the concept of zero. Obviously kids or anyone prior to zero would say there are no apples in the basket, but if you were to ask an ancient Greek philosopher if that meant "no apples" is something worthy of being denoted, they might think you're doing sophistry and trying to elevate nothing to something.

A smart ass kid might reply there are zero oranges in the basket, or zero miniature unicorns. Since the basket is empty, it could have potentially had anything if we're just going to imagine things in baskets. But we don't enumerate over all possible zero items in the basket. And anyway, the basket isn't really empty since it has N air molecules, N fibers or whatever.

The pedantic point I'm making is that counting at zero is a convention we developed for mathematical reasoning when appropriate, but not a starting place for counting things in everyday language.

If an ancient Greek philosopher had three apples in his basket, and I took them away while he wasn't looking, oh, he would definitely find "no apples" something worthy of being denoted.
In Western music theory, intervals are one based. No pitch change is "unison"; one diatonic step is a "major second" and so on. As a result of this silly state of affairs, an octave occurs every 7 notes, even though the root "oct" means eight. Furthermore, a "rule of nines" is needed to invert an interval: e.g. inversion of minor 3rd is a major 6th (exchange major/minor, subtract from 9).
And addition also gets broken. Like a third plus a fourth is a sixth.
True, and it's a great example of how this whole drama is about a practical trade-off, not about a unique Right Answer. If you play piano, the second is the second finger; the fifth is the fifth finger, and it all makes sense. No problem. On the other hand trying to actually count that way (two thirds make a fifth and so forth) is maddening.
Clearly the solution is we need to start numbering fingers from 0.
Maybe that would do it. The only problem is that I'd have four fingers on each hand.
No, just use subtraction for the intervals. Third finger minus first finger = 3 - 1 = 2.

The floors of a building might start at 1, but you go up 1 flight of stairs from the 5th to the 6th floor, not two.

> The 1 makes a lot of sense in a human world, when we count, we start at 1, we talk about the "1st", counting on finger starts with 1, etc.

It is more like counting from 1 is just a leftover from times where zero was not commonly considered as number. Once one have zero, it makes sense to use it as an initial ordinal (see e.g. set theory, where zero is both initial ordinal and initial cardinal number, way before computers).

Another example is time and date, we start counting of days from 1, but counting of hours (at least in 24-hour notation) and minutes from 0.

> Luis started explaining why Lua was 1-index with this sentence: "The 1st argument ...."

Note that for spoken language, it is "The first argument ..." and 'first' is etymologically unrelated to 'one', but related to 'foremost', 'front', so it make sense to use 'first' for the initial item in the sequence even when using counting from 0.

When talking about discrete things, 0 has a specific meaning: it is the absence of things. It does not make sense to count the 'first' element as the 0th. When you encounter the 'first' element, how many elements do you have? 1.

This is of course different for continuous quantities. When counting seconds, for example, we should absolutely start from 0.

> The 1 makes a lot of sense in a human world, when we count, we start at 1

As a kid I learned "one one-thousand, two one-thousand, three one-thousand" when counting time out loud, but at some point I realized this was incorrect. The prefix is the start of the nth second but it isn't complete yet, so for example stopping in the middle of saying "two one-thousand" you actually haven't reached two seconds yet.

My fix was to move the prefix to the end, so I say "one-thousand one, one-thousand two, one-thousand three".

In retrospect maybe I should have used "zero one-thousand, one one-thousand, two one-thousand".

None one-thousand, one one-thousand, two etc

Doesnt sound half bad

I don't think it's necessary incorrect to start at one there. If someone asks you to count out 3 seconds, you're going to say "one one-thousand, two one-thousand, three one-thousand" and only at the end of the "three one-thousand" will you have considered the 3 seconds to have actually elapsed. Basically you're already accounting for the time it's taking you to say it. Which to me seems better because if you do it the other way because it gives a better heads up as to when that second has been reached.
Yes, but "human-focused" 1-based indexing comes at a cost since at the end of the day the CPU has to add (index * element-size) to the array base address to get the address of the indexed element. With a 1-based index there's additional overhead in either having to subtract 1 from the index or to have a wasted "element 0" to avoid the need to do that.
I assure you, that if we counted from zero, there would be an instruction to add and multiply in the same number of cycles as a multiply.
That's not possible - the subtract and multiply need to be consecutive (adjust index before multiply by element size), so even if it was a single instruction it would still take longer than a multiply that didn't have to wait for a preceding subtraction.

The only way to avoid the speed penalty would be either to have a wasted element at offset 0, or to maintain the array base address as (address - (1 * element-size)) to avoid having to subtract 1 from the index when accessing. In the latter case for dynamically allocated arrays the code would still have to do a subtraction to adjust the pointer returned by the memory allocator, but at least that would be a 1-time penalty rather than per-element-access.

Of course this is supposing a high level language where an array is abstraction, not one explicity aliased to a chunk of memory such as C where an array and a pointer to it's first element are interchangeable.

That goes against my intuition. Multiplication in hardware to this day relies on addition. Is one adder going to add an extra cycle? Or would that time be amortized? Take a look at slides 45-46 here. https://acg.cis.upenn.edu/milom/cis371-Spring08/lectures/04_...

Do you know the answer to that question? (I don't, but if someone does, it will settle this issue).

I don't know, but looking at that 3-input add makes me think you may be right and the extra addition/subtraction could perhaps be combined into the multiplication.

OTOH, for arrays who's contents are size 2^n (char, short, int, long) I'm sure the generated code isn't using multiply in the first place.

Anyways, an optimizing compiler could certainly remove much of any overhead added by 1-based indexing .. for an array access in a for loop it could, if necessary, calculate the "base-1" address once at start of loop.

Personally, having grown up with assembler and C, and still using C++ today, I'm quite happy with 0-based.

Human makes a lot of inconsistent thing: We usually think the 1st floor, and the basement as 1st underground floor ( -1), but the floor jumps from 1 to -1!

Also, the time jump from 11AM to 12PM to 1PM! So I think more human friendly sometimes means more confusing.

It's like English pronunciation, there's little logic. I gave up finding logic in these things at an early age, and resorted to memorizing everything instead.
That's true. But as for floor numbering, specifically, it depends on the country: some places have a "ground floor" between the 1st underground floor and the 1st "above ground" floor.
In Europe, the floor level with the ground is called "ground" or "0".
At which number starts the first centimeter on a ruler ?
At which number starts the zeroth centimeter on a ruler?
(comment deleted)
it's the first and it is 0, just like the first index in a C array is 0
I have used 1 based in basic/pascal and 0 based (well in pretty much everything), 0 based effectively no off-by one mistakes. 1 based - common. Most idioms - incl. forward and reverse iterations work better, and are easier to remember with inclusive/exclusive pattern

Other than that - binary AND and power of 2 sized arrays are the backbone of any hashtable. Overall modulus (binary AND) is actually useful.

Ah, yes. Cardinal numbers and ordinal numbers both end in the word "numbers" so they're the same thing. No need to distinguish between having three apples and having the third apple.
If you ask most people to just label their apples, they'd call the first one "1", and so on. Very few people would say, "please pass me apple #0".
And if someone did say that to me, I would pass them no apple, since that's how we use everyday language. And if I knew they were a programmer, I'd first ask them in which programming language they'd wish me to pass the apple.
I believe there's a powerful status quo bias.

Doing the reversal test, if programming languages had all been 1-indexed, then I doubt we'd hear much from people, in 2022, saying "I think the first element should be 0, and the second 1".

> human-focused (1) approach.

TBH humans would have been better of if we were 0-based, it's just a convention. And we have the confusing language where "20th century" means 1900's. If we wanted to bring the 1-indexing we use in language to the fullest extent here to fix that particular issue, time counting would have to start at 1111. Except that won't work once reaching 5-digit years.

If we would start with "zeroeth" instead of "1st", then this would have solved itself and 20th century would mean 20xx's.

I always thought of the machine focus being that the location of the first element was the location of the array plus zero.
> human-focused (1) approach.

TBH humans would have been better of if we were 0-based, it's just a convention. And we have the confusing language where "20th century" means 1900's. If we wanted to bring the 1-indexing we use in language to the fullest extent here to fix that particular issue, time counting would have to start at 1111. Except that won't work once reaching 5-digit years.

If we would start with "zeroeth" instead of "1st", then this would have solved itself and 20th century would mean 20xx's.

I especially don't understand why mathematicians use 1-based indexing (for matrix rows/columns etc...). Like programmers, they should see the advantages of starting at 0 (e.g. the coordinates of subdividing into block matrices are simpler if starting at 0). Mathematicians do start at 0 for the origin of plots, after all.

> If we wanted to bring the 1-indexing we use in language to the fullest extent here to fix that particular issue, time counting would have to start at 1111.

That's funny, but there's actually an elegant way to do it called Bijective numeration (https://en.wikipedia.org/wiki/Bijective_numeration). We're currently living in the 1A22th year.

> when we count, we start at 1

If I ask you to count the number of red balls in a bag with only 3 yellow balls, then the initial count in your head is 0, you inspect the balls one by one, never encountering a red ball, and thus never incrementing the count. And then you pronounce your final count of 0. So that's counting starting from 0.

What you call "starting at 1" is not so much the start as it is the first increment, which need not arise.

There are a lot of cases where a zero index makes sense. In physics and math there are cases where you start the index at 0 and others where you start at 1. And even some where you start at -1. Which you use depends on what is most convenient for the problem at hand.
It has nothing to do with pointers and everything to do with basic computer arithmetic. You can constrain the range of an integer with a bitwise AND operation providing a cheap modulus by power of two. In this regime, zero-based indexing is the natural result. You have to make an adjustment for 1-based. There are whole host of other operations that are simpler with 0-based indexing.

The problem it that most people, even programmers, don't understand how computer arithmetic works and have fantasies of mathematical number lines that the hardware only partially simulates. You see this consistently in the post-Java crowd who think that unsigned integers are some sort of unholy aberration because the languages they've grown up went further to maintain the fictional number line semantics.

I'm fine with 0-based, 1-based or anything-based arrays (I recall it being convenient solving 8queen with pascal), but for Rage-Over-A-Lost-Penny sake, music note intervals are always beyond my understanding.

Same pitched notes are called "interval 1" and there goes thirds, fifths, sevenths... All off-by-one in my base-offset-addressing mind... And then major vs. minor which creates all kinds of "aliased addresses"...

I'd really love a BASE-12 floating number representation. Like 4.00 for the middle C; Chords can be then represented by a tuple of such numbers -- major = [+0.04, +0.07] (some sequencers already do something like that and I'm far better at reading that kind of sequencer data than a sheet)

And then, there's the imperial system.
You do understand that besides thirds, fifths and sevenths, there really are seconds, fourths, sixths, ninths (same as second), elevenths (same as fourth) etc... as well, right? There even are intervals that are not named after a number e.g. the "tritone". The reason the 2nd and the 3rd note in a chord are called third and fifth is because usually chords are made with these intervals instead of dissonant intervals like seconds or fourths. It seems pretty clear you'd already know these things, so can you explain what's your issue with music note intervals?
It's that off-by-one nature of intervals that always bumps me. The difference between note a and b is (a-b+1). Calling an octave "an octave" feels to me like calling a numeric system with digits 0x0-0xf as "base 17"
By your logic, an octave/unison would be "seventh"/"zeroth"? (Note that "octave" literally means "eighth" in Latin.) I would think that could work too but the established terminology is not inconsistent. It's just 1-indexed. A lot of things are 1-indexed, in fact I think almost everything in spoken English is 1-indexed. A lot of mathematics, like number theory, is also 1-indexed. I think software engineers are a little too obsessed with 0-indexed things. I understand that things not being standard is annoying to us but pretending like this somehow makes music terminology broken is going too far.

I don't see how 0x0-0xf could be called base 17. 0xf is 15. Did you mean base 15? I think if mathematics terminology developed differently it could be called base 15. The same way binary is 0 and 1 but we call it base 2 because there are 2 digits, but we could totally call it base 1 too, who cares, it's all convention.

Edsger Dijkstra wrote an interesting article titled 'why numbering should start at 0'. Perhaps not answering the question directly but an interesting read nonetheless.

https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E...

As the article implies, Dijkstra was specifically wrong about FORTRAN, as defined at the time — the '77 standard, when it was still only conscionable to SHOUT 6 alphamerics.

“… you probably know that arrogance in computer science is measured in nano-Dijkstras.” — Alan Kay

"This quote keeps on showing up out of context." — Alan Kay https://news.ycombinator.com/item?id=11799963
Well, I did hear it in context, and have also heard him talking about concocting aphorisms. Kay also said “He did like to pull chains …”
I've never understood the first half of Dijkstra's argument. Unless we are programming a theoretical Turing machine, there will also be a maximum number that can be represented, so the argument about using inclusive bounds for the lower end also applies to the the upper end. So you can't represent all numbers with a single range style. You either end up excluding the smallest number, the largest number, or the empty set. Or you need special notation to handle one of those three cases.

I suppose the 0,1,infinity principle argues that including the empty set and smallest number is more important that including the largest number, so that notation should be preferred by default. And when writing using A<=i<B it is clear enough, but once you remove those symbols and start using them in indexing or function call syntax, like [A:B] or range(A,B), the inconsistency of bounds really breaks my brain, and having to pass a number larger than the largest array index as an upper index range bound gives me nervous ticks. I'd rather use inclusive bounds everywhere and have special syntax for empty set.

Sure but it’s much more common for a range to include 0 than INT_MAX
Interestingly, that Dijkstra article was originally published/circulated in handwritten form. It helps showcase the author's handwriting skills.

https://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF

The handwriting skills were common back in the day... By the way, are you sure that it was Dijkstra himself who handprinted these texts (rather than have, say, his secretary do that)? Because this is not exactly the (beautiful) handwriting as it was taught back then.
He handwrote them, and occasionally typed them (especially early on, I gave up trying to find the transition point). It was something he was known for. If you read the EWD's over the years you'll see the same (or very similar) handwriting throughout, which would not be the case if he had a secretary writing them for him (who wouldn't have been the same person as he moved between countries).

https://en.wikipedia.org/wiki/Edsger_W._Dijkstra#EWD_manuscr...

https://www.cs.utexas.edu/~EWD/ - Pick random ones from different decades and you'll see very similar handwriting.

(Didn't read these, picked two at random):

2001 - https://www.cs.utexas.edu/~EWD/ewd13xx/EWD1307.PDF

1984 - https://www.cs.utexas.edu/~EWD/ewd09xx/EWD901.PDF

(comment deleted)
I liked how in Pascal, one had a lot more flexibility here.

Something like the following IIRC: Type IntArray = Array [-10...10] of Integer;

I find 0-based indexing easier to work with when working with (dynamically sized) multi-dimensional arrays. For example, with 0-based indexing getting an element at (x, y, z) can be done with

  a[z * h * w + y * w + x]
But with 1-based indexing you need to substract one first from each component except the last:

  a[(z - 1) * h * w + (y - 1) * w + x]
Dijkstra's answer (linked in the article) is best:

"When dealing with a sequence of length N, the elements of which we wish to distinguish by subscript, the next vexing question is what subscript value to assign to its starting element. Adhering to convention a) yields, when starting with subscript 1, the subscript range 1 ≤ i < N+1; starting with 0, however, gives the nicer range 0 ≤ i < N. So let us let our ordinals start at zero: an element's ordinal (subscript) equals the number of elements preceding it in the sequence. And the moral of the story is that we had better regard —after all those centuries!— zero as a most natural number."

https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E...

I don't understand why 0 ≤ i ≤ N wouldn't have worked instead, since you already have a less than or equal operator at 0.
That would give a sequence of N+1 elements, though. Confusing if you had a function like range(N), for example.
Why, it would be an inclusive instead of exclusive range. N is the last element, so no reason for N+1.
If we use inclusive ranges where 0 ≤ i ≤ N, then len(range(N)) == N+1.

e.g. len(range(3)) == len([0, 1, 2, 3]) == 4

Wait, why would range(3) start at 0 and not 1 in a 1-based language?
You said: "I don't understand why 0 ≤ i ≤ N wouldn't have worked instead". Was that a typo? Did you mean 0 < i ≤ N?
It could just as easily be 1 ≤ i ≤ N and then I wouldn't have to remember that the lower bound is inclusive and the upper exclusive.
Then the delta of both bounds (N – 1) wouldn’t equal the length of the range (N). The inclusive-exclusive convention is used in order for `end = start + length` to hold.
Why does that matter when the starting point is 1? In that case, you don't need the delta because you have the length already. In the case of a 0-based range you also don't need the delta, though you will want to use the inclusive-exclusive convention so that you get the length "for free".

  1 <= a <= N -- N = length, no delta
  0 <= a < N  -- N = length, no delta
You only calculate the length when dealing with other than 0- or 1-based ranges. There, the inclusive-exclusive convention is very handy as you point out. So if we're fixing the initial offset at either 0 or 1, then use the appropriate convention for that offset. If we let the initial offset float, then the inclusive-exclusive makes sense.
It matters whenever you need to process a proper subrange of an array, and that shouldn’t be different from when the subrange happens to be the whole range. It’s simpler if the same convention is used in all cases.

E.g. in Java, a typical example is that OutputStream has the following two methods, where the first can delegate to the second, and the second (which write the specified subrange of the array) can easily calculate the number of bytes to write:

  int write(byte[] bytes)
  {
      return write(bytes, 0, bytes.length);
  }

  int write(byte[] bytes, int start, int end)
  {
      int length = end - start;
      ...
  }
I addressed that:

> If we let the initial offset float, then the inclusive-exclusive makes sense.

But you replied to someone using inclusive-inclusive for 1-based arrays and complained about the delta not matching the length. Which is a nonsensical complaint, you have the length already why would you need to calculate anything?

In case of the subrange you don’t have the length (or you conversely have the length but not the upper bound), and you don’t want to use different conventions based on whether you process the subrange or the whole range. I’m not sure how I can make it clearer.
Closed ranges (with both ends inclusive) are super annoying to work with. You can represent the empty range (unless you are willing to do [i : i-1]), and they don't compose like half open ones: [a : b) + [b : c) = [a : c).
For some reason I am thinking in the terms of closed ranges if not specified otherwise/doing it for myself. [i:i-1] is how i think of empty ranges, and [i:i] if I want to capture the element i with a range. Also [a:b] + [b+1:c] corresponds more what I want, than [a:b) + [b:c).

I guess the majority of the people are not like this, but arguments like "just look at it how strange it looks" don't do it for me, because it looks natural, and the other way looks complicated.

I'm curious if you would also accept [i:i-2] as a an empty list, or in general anything where the right side is smaller than the left?

If I am working with closed ranges, [i:i-1] looks like the list [i, i-1]. Like [5:2] would be [5, 4, 3, 2].

With [b+1:c] I would feel like I needed to insert a check to ensure b+1 <= c. With the closed ranges the invariant "left is <= right" is maintained automatically. Though I guess it doesn't matter so much if you accept any list with left > right as the empty list.

The issues with compositionality become even more noticeable with floats. Then you would need [a:b] + [b + minimum_float : c], or something like that.

The general term of arithmetic and geometric sequences seem simpler when indexing from 0 rather than 1. I do not think that '1' is more human focused for anything than '0'.
Starting at one led to the whole "2000 is not the millennium" thing, as there is no year zero.
Even more painful is that the 2001 began the 21st century, not the 20th.
Other advantages of zero based indexing, beyond being 'closer to the machine':

It works better with the modulo operator: `array[i%length]` vs `array[(i+length-1)%length+1]`. Or you would have to define a modulo-like operator that maps ℕ to [1..n].

It works better if you have a multi-dimensional index, for example the pixels in an image. With 0 based indexing, pixel `(x,y)` is at `array[x+widthy]`. With 1 based indexing it is at `array[x+width(y-1)]`. You might argue that programming languages should support multi-dimensional arrays, but you still need operations like resizing, views, etc.

This is the reason I always think of when I hear the question
A disadvantage comes to mind. While the following loop works as expected:

    for (size_t i = 0; i < length; i++) ...
The following causes an unsigned integer underflow and is an infinite loop:

    for (size_t i = length - 1; i >= 0; i--) ...
If you have foolishly turned off -W in your build system, that could happen. Otherwise, you get a nice warning pointing out your folly.

  for (size_t i = 0; i < length; i++) {
      size_t j = (length - 1) - i;
      ...
  }
EDIT: change i to j
That's quite broken (you'd want a different variable inside the body, vs clobbering the iteration counter, else this would process the last item in your list, then exit).
Uh no don't reassign the loop variable in the inner scope. Use:

    const j = (length - 1) - i;
in that case. Much safer.
I hope you haven't done this anywhere.

Makes my brain hurt, but I think this will only run through the loop one time looking at the last element of the array.

You should save the old value of i somewhere and restore it back at the very end of the loop. Or simply define a new j like another comment says.
the correct way/idiom to reverse iterate an array is

  for (size_t i = length; i-- > 0; )...
It's surprising how often the issue pops, it works well with both signed and unsigned integers.

(edit) I've started with one based indexing (basic)... mixed with 0 based (assembly), more 1 based (pascal), then more stuff (all zero based). I am, yet, to see a real advantage of a one based indexing... after the initial process.

Is this cache friendly?
It’s not. It was nice on architectures were cache didn’t matter much and were subtracting and comparing to zero was just one instruction (looking at you old core ARM)
Sure, why wouldn't it be? As far as a cache is concerned, I don't think reverse sequential iteration would be any different than forward sequential. The actual RAM accesses may be less optimal if there's some speculative pre-fetching with assumed forward sequential access, but that's conjecture.
i would suspect that the cache prefetch/prediction could use the "velocity" of the memory access to predict the next access; so if the access pattern was going backwards, the "velocity" would be negative, but prefetching would still work if they just followed the predicted pattern.
With some exceptions, hardware prefetch works in terms of ascending accesses. To learn if a particular CPU will prefetch for descending access, benchmarking is essential. Best to use soft prefetch calls if performance is critical.
It is not, unless your compiler is smart enough to recognize reverse iteration and prefetch appropriately.

If performance matters, you should experiment with __builtin_prefetch, which is available in clang and GCC.

In that specific case I'd do the following:

    for (size_t i = n; i-- > 0 ;) ...
Or count from `length` to 1, but subtract 1 in the loop body, or count up and subtract the length in the loop body. Any modern compiler should be able to optimise these to be equivalent.

In the majority of cases, counting down is not necessarily. Nor is ordered iteration. Most languages have a `for each` style syntax that's preferable anyway.

Ah yes, the goes-to operator -->
And the wink operator, so the compiler knows you know the deal
I like how GP is called "operator-name" and instead of doing himself, he makes others joking with operator names. Although I'm not sure if it's altruism or highly manipulative behaviour.
Or alternatively:

  size_t i = length;
  while (i--) ...
Unless I am remembering C wrong, this would work but the

   for (size_t i = length; i-- > 0 ; ) ...
that several other people posted would not execute for index 0. Shouldn't it be this instead?

   for (size_t i = length; --i > 0 ; ) ...
As Jens Gustedt points out[1], the following intentional unsigned overflow works perfectly for downwards iteration (even when length is 0 or SIZE_MAX), though it looks a bit confusing at first:

  for (size_t i = length - 1; i < length; i--) ...
You are also free to start at any other (not necessarily in-bounds) index, just like with ascending iteration.

[1] https://gustedt.wordpress.com/2013/07/15/a-praise-of-size_t-...

the footnote [1] should be [0], just for the sake of this very topic.

Seriously though, while the idiom does work for unsigned integers, it's a bad idiom to learn [makes code reviews harder]. The post-decrement one in the loop body works with everything (signed/unsigned), and it's well known.

Uh I'm confused but don't know c++.

why doesn't that loop end instantly?

I mean length - 1 < length should always be true, right?

Or does it only terminate when the number underflows? Terribly confused here

It’s a condition to run, not a condition to stop
That was my reaction - why should anyone think it might be the latter? Are there languages that do have such a syntax without explicit keywords ("do...until")?
I think lisp or scheme does. I was often confused by that when I was playing with it
It‘s an unsigned int, so past 0 it overflows back to the maximum
Ooh, i see. I wasn't aware that they under/overflow at 0 when they're unsigned. Thanks for broadening my horizon!
It loops while the condition is true. When an underflow happens, it stops being true.
For loops are translatable from:

  for(initialize; condition; increment) { ... }
to:

  initialize;
  while(condition) {
    ...
    increment
  }
(more or less, some scoping things not encompassed by the above; this is also how pretty much every for loop in a C-syntax language works) The condition of a for loop is equivalent to a while loop's condition. So yes, length - 1 < length will be true on the first iteration, which is fine because the loop continues as long as that condition is true.

What the above approach takes advantage of is that when underflow eventually happens you'll have this condition:

  MAXINT < length
Which will terminate it for all possible values of length.
The loop continues until i transitions from 0 to 0 minus 1. 0-1 in this case actually doesn't equal -1 since size_t is an unsigned type, instead it wraps around to be the largest possible positive integer instead. TLDR; yes as you speculate it terminates when the number underflows.
Principle of least surprise violated.

Also, that behavior is not guaranteed. The programmer would need to be aware of how the particular machine in question actually handles that.

Then again, that's C.

Unsigned integer underflow and overflow are both guaranteed to wrap by the C standard.
In the C programming language unsigned integers do not overflow. They wrap. This is well-defined behaviour and the example code is simply incorrect. Most modern compilers will give you a diagnostic for this.
Unless wrapping underflow is sensible for the domain (which it isn’t when representing the size of something), unsigned integers are usually a bad idea.
(comment deleted)
You can always rip a page out of C++’s playbook:

    for (size_t i = length; i > 0; i--) {
        // ...
        item = array[i - 1];
(This is how reverse iterators work in C++.)
A rare case where 1-based indexing is more convenient is complete binary trees laid out breadth-first (as in a standard binary heap): parent is i div 2 and children are 2i and 2i+1 when starting at one and who knows what when starting at zero. But that’s the only one I know.
With 0-based indexing the children are at 2i+1 and 2i+2. The parent is at (i-1) div 2.

Not hard to figure out.

> With 0-based indexing the children are at 2i+1 and 2i+2. The parent is at (i-1) div 2.

> Not hard to figure out.

While that's true, "you just shift by 1" is equally good at all arguments for or against 0-based indexing, so deploying it here probably won't convince.

I was not trying to convince.

That said, the effort of one versus the other is so trivial that there is no point in ever using effort as an argument either way. Doubly so because what seems like effort to us is simple unfamiliarity.

What is important is which one leads to more careless errors in practice. As a trivial example, consistent indentation takes effort, but failing to do it leads to more careless errors. Therefore everyone indents code.

The only data point I've seen on that is the side remark about Mesa in https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E.... That remark, therefore, is the only argument that I care about.

Except 1-based indexing is what we use in normal language. We don't use "zeroeth" or "player (number) zero" etc. And the word "first" is shortened to 1st etc. Personally I think we'd be better off if programming languages stuck to the same convention - off-by-1 errors aren't the hardest problems to deal with but they're still annoying.
The 2 major blunders in programming: 1) Off by one errors.
> "player (number) zero"

That would be because any game worth playing has at least one player... and so it's natural to continue from there. (In terms of language.)

That's confusing ordinal and cardinal numbers. The element with index 0 is the first number. The element with index 1 is the second number, and so on.

Using the term "zeroth" is basically some form of showing off (even though it's kinda fun), but will be utterly confusing when you get to the fifty-second element which is the last in a group of 53 elements.

I'm not confusing them, my point about abbreviating "first" as 1st was that in typical speech we start counting at 1. Nobody says "let's start with item zero on the list". But programmers are stuck with having to say/think "item 0 in the array".
I don't disagree with you. I just don't think a programmer should be confused about the statement "item 0 is the 1'st item".
An incredibly unrare case happens all the time in my work: array[length - 1], or variations of this. Anything involving the last element of the array, and often iterating through the whole array will use something similar at some point.
In many domains code maintenance is more important than hardware costs. In many domains 1-based indexing is a better fit, meaning less conversion code, meaning simpler code. Thus, the best indexing choice depends on the domain and circumstances, as do many controversial questions. Most tend to specialize in specific kinds of domains and over-extrapolate their experience into other domains.
I'd agree so why did languages that allow specifying the base die (other than maybe VBA).
Because changing the base is a great source of bugs.

That said, not all languages have given up on this. For example Julia allows it. https://docs.julialang.org/en/v1/devdocs/offset-arrays/

Ironically I learned this from a discussion of Julia bugs. Apparently changing offsetting of arrays has proven to be a source of bugs in Julia. So maybe someday they will come to the same conclusion as languages like Perl and stop allowing it.

I'd argue 0-base is a source of bugs too! Ideally we'd be able to catch more array indexing bugs at compile time - there are definitely cases where it should be possible to determine that arrays are being incorrectly indexed via static analysis.
The problem is that libraries which assume 0-base break when you have a 1-based array. And vice versa. Trying to combine libraries with different conventions becomes impossible.

Therefore changing the base leads to more bugs than either base alone.

That said, the more you can just use a foreach to not worry about the index at all, the better.

Of 0-based and 1-based, the only data point I have is a side comment of Dijkstra's that the language Mesa allowed both, and found that 0-based arrays lead to the fewest bugs in practice. I'd love better data on that, but this is a good reason to prefer 0-based.

That said, I can work with either. But Python uses 0-based and plpgsql uses 1-based. Switching back and forth gets..annoying.

I'd expect the compiler not to let you to pass a 0-based array to a library function expecting a 1-based array. I'm pretty sure that's how it worked with Visual Basic, which was the only language I ever used such a feature in.
You are demanding a lot from the type system.

Search for OffsetArrays in https://yuri.is/not-julia/ for practical problems encountered in trying to make this feature work in a language whose compiler does try to be smart.

The type system does tell you if this is used. `::OffsetArray`.
Yes, but did the programmer tell the type system that they are expecting an OffsetArray, they have tested it, and it will work correctly?

The existence of a mechanism does not guarantee its correct use. As that link demonstrates.

I think the 1-indexing folks would have to argue that a%b should return a value from 1 to b inclusive. This does make the same sort of intuitive sense as 1-indexing. For example we number clocks from 1 to 12.
% is defined as (mostly) a remainder operator. I don't think you want to change the semantics of division itself.
But interestingly, the number at the top is 12, not 1.
No we don't. Clocks start at 0. what time is it when it's half an hour after midnight? 00:30.
but nobody "says" zero o'clock - it's always twelve o'clock!

and the example is in 24hr format - which needs to have the 00 to differentiate it from being 12:30. But if you write in 12hr format, you don't ever use 00 - it's always 12:30am or 12:30pm

First of all, you don't. Or I guess most Americans don't. I do, or most Europeans do. 12.30am and 12.30pm feels just very wrong in Europe.

But yea, I do agree with you. we don't say 0, we say 12, no matter if it's noon or midnight. That's because we humans avoid saying zero when we mean zero.

It's the same with other comments here, talking about counding seconds, we say "ok go, one, two,.. and not "zero, one, two,..". We say 3 months old baby, and not 0 years old baby.

We use 0-index in so many things, we just avoid saying the word "zero", and we use other names or other units to avoid that word.

I'm from Sweden, and I certainly would say 00:15 (as zero fifteen). Although the time at exactly 00:00 would be called midnight.
Another advantage is with ranges: 0-based indexing and exclusive ranges work well. This is apparent with cursor position in text selection

Consider:

    Characters       h e l l o
    Cursor index    0 1 2 3 4 5
    Char index       0 1 2 3 4
    Range [0,3)     [0,1,2]
    Range [2,5)         [2,3,4]
    Range [1,1)       []
If we used 1-based indexing and exclusive ranges, it leads to ranges where the end index is greater than the string's length...

    Characters       h e l l o
    Cursor index    0 1 2 3 4 5
    Char index       1 2 3 4 5
    Range [1,4)     [1,2,3]
    Range [3,6) (!)     [3,4,5]
    Range [2,2)       []
but if we use inclusive ranges, it leads to ranges where the end index is less than the start index...

    Characters       h e l l o
    Cursor index    0 1 2 3 4 5
    Char index       1 2 3 4 5
    Range [1,3]     [1,2,3]
    Range [3,5]         [3,4,5]
    Range [2,1) (!)   []
Also:

    Characters            h e l l o
    Cursor index         0 1 2 3 4 5
    0-based range [0,3)  [0,1,2]
    1-based range [1,4)  [1,2,3]
for the 0-based range [0, 3), the left array bracket is at cursor index 0, and the right bracket is at index 3. With 1-based indexing it doesn't work like that because the range is [1, 4)
This is the bane of my existence working with Lua.

Iterating an array or adding to the end are fine, we have ipairs and insert for that, but ranges on strings I'm constantly having to think harder and write more code than necessary.

I love the language, wouldn't trade it for another, but the 1-based indexing on strings, which represents an empty string at position 3 as (3,2), it's egregious.

Not as egregious as a dynamic language where 0 is false though.

> a dynamic language where 0 is false though.

well, C also considers 0 being false (and you can argue that C is "dynamic"!).

Yeah, offsets are just easier to mathematically manipulate than ordinals. It's not just pointer arithmetic where it matters that item i corresponds to start+i*step. Any time you want to convert between integer indices and general linearly-spaced values, 0-based indexing is more convenient.
Indexes start at 1. Offsets start at 0.

Arrays in programming use offsets.

    for(int i=0;i<10;i++) 
    {
       array[i] = x;
    }
The error here is calling it 'i' for index. It should be 'o' for offset.
I've discussed this a lot in real world in a different field: apartment floors. In Japan (where I live) they are 1-indexed, where the floor on the ground is number 1, while in Spain (where I am from) they are 0-indexed, where the floor on the ground is number 0.

Both have inconsistencies, like in Spain you might have a "middle ground" (entresuelo) which is neither 0 nor 1, but sits between, and is normally commercial or non-livable, reserving the 1 to the first floor where people live. I like that system better though because it usually goes 1 => 0 => -1 (underground), while in Japan you go 2 => 1 => -1, so I feel like it's missing a floor. You could justify it though as 0 being the ground line, so +1 is "the first above the floor and -1 is "the first below the floor", but I still prefer having each floor to be a natural consecutive number.

It seems like most newer buildings in the US and EU have "G" or "L" be the ground floor (0 / PB), and then the floor above is "1".

In older buildings, the ground floor is usually "1" - which means the floor above has to be "2".

I (US) don’t remember seeing the second floor labeled 1. I often see G, 2, 3
English also has that "mezzanine" concept, for partial floors with a balcony over the main floor. You see it sometimes on elevators as an M.
Arrays in Ada start at the index based on the index type of the array. You can even use an enumeration type as the index:

    type Day is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
    type Hours is array (Day range <>) of Natural;

    V : Hours (Monday .. Friday);
Which index type you should use depends on the problem that you're trying to model. You can find out the lower/higher end of a type/variable with the 'First and 'Last attributes.

IIRC there's an RFC though to force the lower end of the index to a certain value like 1 or any other number/enum.

I always wonder why such fundamental features were not adopted by all languages.
Wow, I never really looked at Ada code. Been using VHDL for the past 2 years a lot and when I looked at your code I was like: 'huh strange, this looks a lot like VHDL'.

Turns out both were invented by the DoD.

>Due to the Department of Defense requiring as much of the syntax as possible to be based on Ada, in order to avoid re-inventing concepts that had already been thoroughly tested in the development of Ada,[citation needed] VHDL borrows heavily from the Ada programming language in both concept and syntax. - https://en.wikipedia.org/wiki/VHDL

Maybe I should pick up Ada soon. That could be a fun journey! (I really love writing VHDL)

This reminds me of arguments about male -> female and man -> woman (and human etc). Without offending anyone's delicate sensibilities - you may note that there is a short version and long version of those words, suggesting that the longer version is derivative of the short version.

However, when you examine the origin of the words, that is not the derivation. The derivation and path to English is quite complicated. You may feel a sense of righteous indignation or righteous repudiation on this topic. I would gently suggest deferring that feeling because I think the story is very interesting to follow.

That is not the end of the story though. The reason those pairs of words stuck around is almost certainly because it makes a consistent mental picture.

Bringing it back to the topic at hand - "Why we use 0" - because it works. There are many times when it has been evaluated, and at least to the people who design languages, it is more mentally appealing.

https://www.etymologynerd.com/blog/man-vs-woman

https://medium.com/interesting-histories/interesting-histori...

I really fail to understand what the etymology of sex and gender classifications has to do with array based indexing.
> "Bringing it back to the topic at hand - "Why we use 0" - because it works. There are many times when it has been evaluated, and at least to the people who design languages, it is more mentally appealing."

The linked post is not just about a technical decision, it's about a social history.

The conclusion of the linked post:

> "Lessons

Things can have more than one cause. Don’t trust easy explanations.

Always look for information that refutes your theory, not just information that supports it. Hoye stopped as soon as he had a satisfying answer and didn’t keep researching.

Don’t be a dick.

It is tragically easy to trick me into doing free research."

Funny, just a few hours ago I asked myself the related question "Should indices start with 0 or 1?" (not for the first time). I pretty much switch my opinion as many times as I think about it.

Here is a nice discussion on stackoverflow https://cseducators.stackexchange.com/questions/5023/why-do-...

The first answer nicely retells the dijkstra argument: integer ranges should be described using half open intervals, and [n, m) is nicer than (n, m]. Furthermore, [0, n) is nicer than [1, n+1), and that's that.

The second answer makes the observation that there is a difference between indexing and counting, and that even in daily life often the first element is indexed by 0.

Still, I rather like indexing the first element of a sequence with 1.

The article this one points to has had a few threads:

The origin of zero-based array indexing - https://news.ycombinator.com/item?id=6879478 - Dec 2013 (107 comments)

Zero-based arrays and the mythology of programming - https://news.ycombinator.com/item?id=6708409 - Nov 2013 (1 comment)

Citation Needed - https://news.ycombinator.com/item?id=6595521 - Oct 2013 (2 comments)

Threads about the EWD mentioned by jaapsen01:

Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=22162705 - Jan 2020 (220 comments)

Dijkstra's argument on why numbering should start at zero [pdf] - https://news.ycombinator.com/item?id=17850441 - Aug 2018 (1 comment)

Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=17765034 - Aug 2018 (63 comments)

Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=13186225 - Dec 2016 (216 comments)

Why numbering should start at zero (1982) - https://news.ycombinator.com/item?id=9761355 - June 2015 (47 comments)

Dijkstra: Why numbering should start at zero - https://news.ycombinator.com/item?id=777580 - Aug 2009 (71 comments)

Also these. Others? I'm a little surprised there aren't more.

Why do we count starting from zero? - https://news.ycombinator.com/item?id=17923391 - Sept 2018 (1 comment)

Why C Arrays Start at Zero: I Don't Know - https://news.ycombinator.com/item?id=11228267 - March 2016 (3 comments)

Why C Arrays Start at Zero: I Don't Know - https://news.ycombinator.com/item?id=11114704 - Feb 2016 (2 comments)

Perhaps also worth adding that the linked article (the one titled "Citation Needed") itself originally had hundreds of comments beneath it, many quite argumentative, which seem to be now missing - I guess lost in a site update. It was a very contentious article.

I'm sympathetic to its author's approach - we talk about indexing and it's a strangely fascinating topic but the history of it hasn't been all that well dug out. We do indeed tend to respond to articles like this by going "well obviously [x]-based is good because" - you can see that at work in this discussion here - but those are not necessarily the true historical reasons. But I think the author made a leap too far, the article was a bit too brittle, and it landed in a slightly too argumentative spot.

"Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration." -- Stan Kelly-Bootle
This comes up when giving fractional coordinates into a grid. Is (1,1) the upper-left corner of the upper-left grid box, the center, or the lower-right (or the center of the next grid box)? Alternately, is the upper-left corner of the upper-left grid box (-0.5, -0.5), (0.0, 0.0), (0.5, 0.5), or (1.0, 1.0). I've seen all four conventions used in different places, depending on whether the grid boxes themselves are numbered starting at 0 or 1, and when extended to fractional if it makes more sense to have the corner or the center of the grid box take the value of the box itself.
Which is how we got the dx9 half pixel offset in fragment shaders. luckily not replicated in newer revisions.
Array indices denote offsets from the base address. The first element is at the base address of the array, hence the offset is zero. It avoids a conditional when resolving the address, introducing it would slow down all forms of memory access across the language domain.
Yet somehow Fortran manages to be highly performant.
In Mathematica/Wolfram Languege, the part 0 of an expression is reserved for the head of the expression: f[x, y, z][[0]] == f. Or g[f][x, y][[0, 1]] == f.