44

What is an off-by-one error? If I have one, how do I fix it?

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Dina
  • 515
  • 1
  • 5
  • 6

6 Answers6

83

An off-by-one error is for example when you intend to perform a loop n times and write something like:

for (int i = 1; i < n; ++i) { ... }

or:

for (int i = 0; i <= n; ++i) { ... }

In the first case the loop will be executed (n - 1) times and in the second case (n + 1) times, giving the name off-by-one. Other variations are possible but in general the loop is executed one too many or one too few times due to an error in the initial value of the loop variable or in the end condition of the loop.

The loop can be written correctly as:

for (int i = 0; i < n; ++i) { ... }

A for loop is just a special case of a while loop. The same kind of error can be made in while loops.

Adham
  • 121
  • 7
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
9

An off-by-one error is when you expect something to be of value N, but in reality it ends up being N-1 or N+1. For example, you were expecting the program to perform an operation 10 times, but it ends up performing 9 or 11 times (one too few or one too many times). In programming this is most commonly seen happening when dealing with "for" loops.

This error happens due to a misjudgement where you do not realize that the number you are using to keep track of your counting may not be the same as the number of things you are counting. In other words, the number you are using to count may not be the same as the total of things you are counting. There is nothing that obligates both things to be the same. Try to count out loud from 0 to 10 and you end up saying 11 numbers in total, but the final number that you say is 10.

One way to prevent the problem is to realize that our brain has a tendency (maybe a cognitive bias) to make that error. Keeping that in mind may help you identify and prevent future situations. But I guess that the best thing you can do to prevent this error is to write unit tests. The tests will help you make sure that your code is running as it should.

Pedro Pinheiro
  • 1,059
  • 14
  • 32
5

Say you have the following code featuring an array and a for loop:

char exampleArray[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };

for(int i = 0; i <= 11; i++)
{
  print(exampleArray[i])
}

See the issue here? Because I counted my array to have eleven characters in it, I have set my loop to iterate eleven times. However, arrays start at zero in most languages, meaning that when my code goes to print

exampleArray[11]

I will get an index out of bounds error because the array in the example has no value at index eleven.

In this case, I can fix this easily by simply telling my loop to iterate one fewer times.

The easiest way to debug this issue is to print out your upper and lower bounds and see which value generates an index out of bounds error, then set your value to be one greater or one fewer than it is throughout your entire iteration.

Of course, this assumes the error is generated by a loop going one over or one less than the bounds of an array, there are other situations where an index out of bounds error can occur, however, this is the most common case. An index out of bounds will always refer to trying to access data where data does not exist due to the boundaries past not being within the boundaries of data.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Vapidant
  • 2,532
  • 2
  • 12
  • 26
  • Nice answer! What language is this referring to? It looks like C to me, but I don't know. – S.S. Anne Aug 18 '19 at 02:01
  • @JL2210 This generally applies to most languages (I dare say all languages but I won't because I can't be 100% sure that there aren't languages that this error could mean something else) In my example I was doing Java but then did a generic print rather than System.out.print() because I got lazy and decided to keep it generic. Like I said though, this concept should carry throughout most languages you work with. – Vapidant Aug 18 '19 at 04:16
  • Index out of bounds errors don't occur in C, they just cause undefined behavior. – S.S. Anne Aug 18 '19 at 04:18
  • 1
    @JL2210 I don't work in C often so I was unaware of this, I just looked this up and did some testing though and it seems you are correct. That being said, I'd argue this is still an out of bounds error even if it's not technically throwing the out of bounds error. In C it would appear that when trying to access something out of the bounds that it will either return some random memory that is still owned by the software leading to some random unexpected return, or the software may try and retrieve a memory it does not own which will lead to a crash. Either way, this concept still applies IMO. – Vapidant Aug 18 '19 at 04:23
  • Or it could unknowingly format your hard drive. Undefined behavior means "no restrictions"; your program could cause your computer to burn up and that behavior would still be valid. – S.S. Anne Aug 18 '19 at 04:55
2

A common off-by-one confusion arises because some languages enumerate vectors from zero (C, for example) and other languages from one (R, for example). Thus, a vector x of size n has members running from x[0] to x[n-1] in C but from x[1] to x[n] in R.

You are also confronted with the off-by-one challenge when coding the common idiom for cyclic incrementation:

In C:

i = (i+1)%n

In R:

i <- (i-1)%%n + 1
S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Niels Holst
  • 586
  • 4
  • 9
2

Off by one error (sometimes called OBOE) crop up when you're trying to target a specific index of a string or array (to slice or access a segment), or when looping over the indices of them.

If we consider Javascript as an example language, indexing starts at zero, not one, which means the last index is always one less than the length of the item. If you try to access an index equal to the length, the program may throw an

"index out of range" reference error

or

print undefined.

When you use string or array methods that take index ranges as arguments, it helps to read the documentation of that language and understand if they are inclusive (the item at the given index is part of what's returned) or not. Here are some examples of off by one errors:

let alphabet = "abcdefghijklmnopqrstuvwxyz";
let len = alphabet.length;
for (let i = 0; i <= len; i++) {
  // loops one too many times at the end
  console.log(alphabet[i]);
}
for (let j = 1; j < len; j++) {
  // loops one too few times and misses the first character at index 0
  console.log(alphabet[j]);
}
for (let k = 0; k < len; k++) {
  // Goldilocks approves - this is just right
  console.log(alphabet[k]);
}
shuberman
  • 1,416
  • 6
  • 21
  • 38
2

A simple rule of thumb:

int i = 0; // if i is initiated as 0, always use a < or > in the condition
while (i < 10) 
    System.out.printf("%d ", i++);
int i = 1; // if i is initiated as 1, always use a <= or >= in the condition
while (i <= 10)
    System.out.printf("%d ". i++);
luma
  • 79
  • 1
  • 7