Помилка на одиницю
Помилка на одиницю (off-by-one error) — це логічна помилка, яка містить число, яке відрізняється від запланованого значення на +1 або −1. Це часто відбувається при програмуванні, коли ітерація циклу повторюється на один раз більше або на один раз менше ніж потрібно.
Основні причини це:
- неправильне розуміння нестрогої нерівності (≤) як умови виходу з циклу в той час як потрібна сувора нерівність (<), або навпаки.
- плутанина, пов'язана з використанням нумерації від нуля
Приклади
ред.Помилка парканного (або телеграфного) стовпа
ред.Помилка парканного стовпа (іноді її називають помилкою телеграфного стовпа, ліхтарного стовпа або паркану ) — це особливий тип помилки «на одиницю». Ця помилка описана дуже давно, наприклад з'являється в роботах Вітрувія.[1]
Задачі, які можна сформулювати як :
Пряма задача Вам потрібно побудувати паркан 10 метрів завдовжки. Через кожен метр повинен стояти стовп. Скільки стовпів вам необхідно? | Обернена задача Якщо у вас є 10 стовпів, які потрібно встановити в паркані кожен метр, то яка буде довжина паркану? | |||
Звісно відповідь залежить від конструкції паркану, і в нашому випадку, якщо ми вказуємо що паркан починається з стовпа, потім йде секція паркану, далі стовп, і так далі, та в кінці паркан закінчується стовпом.
Швидка інтуїтивна відповідь що для прямої задачі потрібно 10 стовпів, а для оберненої - довжина 10 метрів. Але такі відповіді є неправильними, оскільки довжина паркана дорівнює кількості секцій, а кількість секцій на одну менше ніж стовпів. Якщо паркан має 10 секцій то він має довжину 10 метрів, але має 11 стовпів. Та обернений варіант 10 стовпів це 9 секцій тобто 9 метрів.
Помилки в подібних задачах виникають через підрахунок предметів, а не проміжків між ними, або навпаки, або нехтування питанням про те, чи слід рахувати один чи обидва кінці ряду.
Приклад цієї помилки може виникнути в мовах розрахунків MATLAB, а також numpy в функціях linspace()
та arange()
.
Функція linspace
має параметри linspace(lower_value, upper_value, number_of_values )
Програміст, який неправильно зрозуміє третій параметр, та захоче отримати масив який починається з 0, закінчується 10, проміжками по 2 одиниці - [0, 2, 4, 6, 8, 10]
, подумає що третій параметр повинен бути (10-0)/2 = 5
.
Але linspace(0, 10, 5)
повертає масив з 5 значеннями, розраховуючи однакові проміжки [0, 2.5, 5, 7.5, 10]
.
А інша функція numpy
, arange(0, 10, 2)
поверне значення [0, 2, 4, 6, 8]
що не включає останнє значення 10.
Така помилка може виникати не лише при вимірюванні довжини чи програмуванні. Наприклад, піраміда часу, що повинна складатись з 120 блоків, та заплановано що кожні 10 років будується новий блок. Автор конструкції хотів продемонструвати що таке 1200 років, але піраміда буде побудована за 1190 років. Одна з найдавніших помилок на одиницю також пов'язана з часом, коли на початку впровадження юліанського календаря протягом 36 років обчислювали високосні роки неправильно, оскільки священики вирішили що високосний рік повинен бути кожні три роки, а не через кожні три роки.
Ітерація по масиву
ред.Розглянемо масив елементів, з елементами від m до n (включно) які потрібно обробити. Скільки там об'єктів? Інтуїтивно зрозумілою відповіддю може бути n − m, але вона відрізняється на одиницю, демонструючи помилку парканного стовпа тому що правильна відповідь n − m + 1. Саме тому діапазони в програмуванні часто представлені напіввідкритими діапазонами. Діапазон [m, n] (включно) представляється [m, n + 1) (від m включно до n виключно), щоб уникнути таких помилок.
Наприклад, цикл, який повторюється п’ять разів (від 0 до 4 включно), можна записати як напіввідкритий інтервал від 0 до 5:
for (index = 0; index < 5; index++)
{
/* тіло цикла */
}
Перша ітерація циклу виконується в перший раз з index = 0, потім index = 1, index = 2, index = 3 і, нарешті index = 4.
В той момент коли index зростає до 5, умова index < 5 стає хибною, і цикл завершується. Однак, сама процедура перевірки умови, порівняння <= (менше або дорівнює) виконується шість разів. Якщо перевірка буде викликати якусь функцію або дію, то ця дія буде виконана шість разів.
Схожим образом може виникнути така помилка може виникнути, якщо замість циклу while-do
використовувати цикл do-while (або навпаки).
Див. також
ред.Примітки
ред.- ↑ Moniot, Robert K., Who first described the "fence-post error?", Fordham University, архів оригіналу за 5 березня 2016, процитовано 7 липня 2016.