В практике разработки на платформе 1С:Предприятие часто возникает необходимость работы с типизированными коллекциями данных. При программном создании объекта ТаблицаЗначений мы стремимся жестко определить типы ее колонок, чтобы избежать ошибок при дальнейшей обработке. Однако здесь разработчиков подстерегает неочевидная ловушка: при записи корректного ненулевого числа (например, единицы, прочитанной из файла DBF или полученной расчетным путем) в колонке таблицы значений внезапно оказывается 0.
В этой статье мы подробно разберем, почему возникает такое поведение платформы, как именно 1С рассчитывает разрядность чисел «под капотом», и рассмотрим несколько надежных способов решения данной проблемы.
Рассмотрим классический пример создания типизированной колонки, с которым столкнулся автор темы. Нам необходимо создать таблицу значений с колонкой OBRAZ, которая должна хранить целое число длиной в один символ (например, двоичный флаг: 0 или 1). Разработчик пишет следующий код:
КЧ_1_ = Новый КвалификаторыЧисла(1, 0);
Массив = Новый Массив;
Массив.Добавить(Тип("Число"));
ОписаниеТиповЧ_1_ = Новый ОписаниеТипов(Массив, , , КЧ_1_);
ТЗ_ = Новый ТаблицаЗначений;
ТЗ_.Колонки.Добавить("OBRAZ", ОписаниеТиповЧ_1_);
НС = ТЗ_.Добавить();
НС.OBRAZ = Число(ДБФ1.OBRAZ); // В источнике ДБФ1.OBRAZ хранится единица
При пошаговой отладке выясняется странная деталь: в переменной-источнике ДБФ1.OBRAZ гарантированно находится число 1. Однако сразу после выполнения строки присваивания значение НС.OBRAZ принимает значение 0. При этом, если присвоить значение напрямую через литерал НС.OBRAZ = 1, в некоторых случаях платформа может отобразить единицу в отладчике, но при дальнейшей обработке или выгрузке данных значение все равно превращается в ноль.
Давайте разберемся, почему так происходит.
Главная причина кроется в поведении конструктора КвалификаторыЧисла и механизме резервирования разрядов платформой 1С. Когда мы объявляем квалификатор следующим образом:
КЧ_1_ = Новый КвалификаторыЧисла(1, 0);
Мы передаем в него два параметра: общую длину числа (1) и количество знаков после запятой (0). Третий параметр конструктора — ДопустимыйЗнак — не указан. По умолчанию платформа 1С принимает его равным значению системного перечисления ДопустимыйЗнак.Любой. Это означает, что число может быть как положительным, так и отрицательным.
Для представления знака числа (плюса или минуса) платформе требуется зарезервировать ровно **один разряд**. Расчет доступной разрядности для целой части числа в 1С происходит по следующей строгой формуле:
Разрядность целой части = Общая длина - Дробная часть - Позиция под знак (если знак "Любой")
Подставим наши значения в эту формулу:
Разрядность целой части = 1 (общая длина) - 0 (дробная часть) - 1 (знак) = 0 знаков
Таким образом, для хранения самого числа у платформы остается ровно 0 разрядов! Любое целое число, которое мы попытаемся записать в эту колонку (будь то 1, 5 или 9), из-за нулевой разрядности целой части будет автоматически округлено платформой до 0. Это фундаментальное ограничение типизации данных в 1С.
Для устранения этой проблемы мы можем пойти несколькими путями в зависимости от архитектурных требований к разрабатываемому алгоритму. Рассмотрим каждый способ подробно.
Если в вашей колонке гарантированно будут храниться только положительные числа или нули (например, статусы, флаги, булевы маркеры 0 и 1), позицию под знак резервировать не требуется. Для этого при инициализации квалификатора необходимо явно передать третий параметр:
// Указываем допустимый знак как "Неотрицательный"
КЧ_1_ = Новый КвалификаторыЧисла(1, 0, ДопустимыйЗнак.Неотрицательный);
ОписаниеТиповЧ_1_ = Новый ОписаниеТипов("Число", , , КЧ_1_);
ТЗ_ = Новый ТаблицаЗначений;
ТЗ_.Колонки.Добавить("OBRAZ", ОписаниеТиповЧ_1_);
При таком подходе формула расчета примет вид: 1 - 0 - 0 = 1. Под целую часть числа будет выделен полноценный 1 разряд. Теперь цифры от 0 до 9 запишутся в таблицу значений корректно и без искажений.
Если ваше число все-таки может принимать отрицательные значения (например, -1), вам необходимо увеличить общую длину числа как минимум до двух символов, чтобы один символ остался под знак, а второй — под цифру:
// Увеличиваем общую длину до 2, оставляя знак "Любой" по умолчанию
КЧ_1_ = Новый КвалификаторыЧисла(2, 0);
ОписаниеТиповЧ_1_ = Новый ОписаниеТипов("Число", , , КЧ_1_);
В данном случае расчет будет следующим: 2 - 0 - 1 = 1. Мы получим возможность записывать в колонку числа в диапазоне от -9 до 9.
Если создаваемая ТаблицаЗначений является временным объектом в рамках одной процедуры, не передается в жестко типизированные схемы (например, в СКД или внешние СУБД) и используется только для промежуточных вычислений, лучшим решением будет отказаться от явного указания типов при добавлении колонки:
ТЗ_ = Новый ТаблицаЗначений;
// Добавляем колонку без указания ОписанияТипов
ТЗ_.Колонки.Добавить("OBRAZ");
При таком подходе колонка получит неопределенный тип. Платформа 1С автоматически определит тип значения непосредственно в момент записи первой строки. Это полностью исключит любые проблемы с округлением и усечением разрядов, избавив вас от необходимости ручной настройки квалификаторов.
Проанализировав данную проблему, мы выяснили, что неявное поведение платформы 1С при работе с объектом КвалификаторыЧисла может приводить к потере данных. Всегда обращайте внимание на количество разрядов, выделяемых под знак числа. Если вы работаете с положительными числами-индикаторами, всегда явно используйте параметр ДопустимыйЗнак.Неотрицательный.