Миграция COBOL COMP-3 без потери копейки
Миграция COBOL COMP-3 провалится, если изменятся масштаб, знаки, округление или недопустимые байты. Опишите и проверьте каждый результат.

У переписывания денежной системы есть один критерий приемки: одинаковые допустимые входные данные должны давать одинаковую сумму, знак, статус и хранимое представление везде, где оно остается частью интерфейса. Расхождение в одну копейку нельзя считать косметическим дефектом. На миллионе записей оно может изменить итог главной книги, очередь исключений, процентный диапазон или файл, который принимает следующая программа.
Опасно полагать, что полю COBOL достаточно подобрать тип в современном языке. Смысл поля задают PICTURE, USAGE, параметры компилятора, арифметические операторы, принимающие поля, формат файла и порой ошибочные данные, которые система терпела десятилетиями. PIC S9(7)V99 COMP-3 описывает знаковый целочисленный коэффициент с неявным масштабом два и контракт упакованного хранения. Если свести его к обычному числу, пропадет как минимум половина информации.
Я видел, как команды дольше спорили о decimal и double, чем искали MOVE, который на самом деле отбрасывал дробные разряды. Такой спор начинается слишком поздно. Сначала восстановите числовой контракт. Затем выберите целевое представление, способное его соблюдать, и прогоняйте через обе системы один трафик, пока не объясните каждое расхождение.
PICTURE входит в значение поля
PICTURE задает число цифр, масштаб, возможность знака и иногда масштабные позиции, которые не занимают места. Эти свойства не сводятся к форматированию.
Рассмотрим объявления:
01 INVOICE-AMOUNT PIC S9(7)V99 COMP-3.
01 TAX-RATE PIC S9(3)V9(4) COMP-3.
01 WHOLE-DOLLARS PIC S9(9) COMP-3.
01 SMALL-RATIO PIC SV9(6) COMP-3.
V обозначает предполагаемую десятичную точку. Ни в памяти, ни на диске отдельного байта для нее нет. INVOICE-AMOUNT хранит девять десятичных цифр и знак, поэтому коэффициент 123456789 означает 1234567.89. У TAX-RATE масштаб четыре. У WHOLE-DOLLARS масштаб ноль. У SMALL-RATIO нет целых позиций, поэтому коэффициент 123456 означает 0.123456.
В реестре миграции для каждого элементарного числового поля следует записать хотя бы (signed, precision, scale, usage, byte length). Рядом сохраните исходное объявление и формат содержащей его записи. В copybook встречаются REDEFINES, OCCURS, имена условий и групповые перемещения. Поэтому поле не всегда можно трактовать отдельно от соседних байтов.
Символ P требует отдельного правила. Он описывает неявные масштабные позиции, которые не хранятся. В документации IBM Enterprise COBOL есть примеры PPP999, где хранимые цифры соответствуют значениям от нуля до .000999, и S999PPP, где ненулевые значения идут с шагом в тысячу. Если преобразователь считает только хранимые цифры, он ошибется в арифметическом масштабе. Нельзя выводить DECIMAL(p,s) только из длины в байтах.
Создайте машиночитаемый каталог, а не таблицу, которая со временем разойдется с кодом. Полезная запись выглядит так:
{
"qualifiedName": "CLAIM-REC.PAID-AMOUNT",
"picture": "S9(7)V99",
"usage": "COMP-3",
"bytes": 5,
"precision": 9,
"scale": 2,
"signed": true,
"storage": "packed-decimal"
}
С этого артефакта начинается контракт переписывания. Он также выявляет частую ошибку парсера: девять цифр упакованного десятичного поля занимают пять байтов, потому что последнее полубайтовое значение хранит знак.
Разберите псевдонимы до назначения смысла. В одной ветке REDEFINES те же пять байтов могут быть суммой, а в другой заполнителем или датой. Дискриминатор, который выбирает ветку, входит в числовой контракт. Если новый слой ввода сразу декодирует все возможные ветки, он способен отвергнуть правильную запись: числовые байты одного формата оказываются текстом другого. Фиксируйте управляющее условие, а не только пересекающиеся смещения.
Групповые операции требуют внимания по обратной причине. MOVE OLD-GROUP TO NEW-GROUP копирует байты без правил числового преобразования для отдельных полей. Замена на поэлементное отображение объектов может нормализовать знаки, изменить заполнение или декодировать поле, которого исходная программа не касалась. Сначала определите, где операция работает с байтами, а где с числами, и лишь потом признавайте типизированный объект эквивалентом.
Байты COMP-3 нужно декодировать явно
Упакованный десятичный формат помещает по две цифры в байт. Исключение составляет младший полубайт последнего байта, где хранится знак. Целевая среда должна явно проверять и декодировать полубайты на каждой внешней границе.
Для PIC S9(5)V99 COMP-3 значение -12345.67 с коэффициентом -1234567 может выглядеть так:
12 34 56 7D
Полубайты читаются как 1 2 3 4 5 6 7 D. Первые семь содержат цифры. Последний D означает минус. Обычное положительное значение заканчивается на C, а беззнаковое упакованное поле часто на F. В реальных системах встречаются другие коды, зависящие от параметров компилятора и пути формирования данных. Поэтому декодеру нужна заданная политика, а не снисходительное шестнадцатеричное преобразование.
Длина для n хранимых цифр равна floor(n / 2) + 1 байтам. При четном числе цифр первый полубайт служит заполнителем. Его тоже нужно проверять. IBM указывает, что NUMCHECK(PAC) проверяет упакованные цифры и знаки, когда поля используются как отправители, а при четном числе цифр также проверяет неиспользуемые биты. Новый декодер должен решить, отклонять ли запись при неверном заполнении, отправлять ли ее в карантин или воспроизводить явно описанную терпимость старой системы.
Граница хорошо видна в таком псевдокоде:
decodePacked(bytes, precision, scale, signed):
nibbles = splitEachByte(bytes)
signNibble = nibbles.removeLast()
if precision is even:
require nibbles.removeFirst() == 0
require count(nibbles) == precision
require every nibble is between 0 and 9
sign = decodeSign(signNibble, signed, configuredSignPolicy)
coefficient = sign * decimalDigitsToInteger(nibbles)
return FixedDecimal(coefficient, scale)
Храните коэффициент как целое число, а масштаб как метаданные. Тогда 123.40 отличается от значения с плавающей точкой без масштаба, даже если экран потом покажет 123.4. Кодировщик сможет точно воспроизвести запись фиксированной ширины.
Напишите кодировщик независимо, затем проверьте encode(decode(bytes)) для каждого допустимого канонического входа. Проверьте и неканонические варианты, разрешенные политикой. Числовая эквивалентность может позволять заменить положительный знак F на C, но побайтовый паритет нарушится, если потребитель ждет исходную форму. Для точного кругового преобразования сохраняйте исходный код знака или все сырое поле рядом с декодированным числом.
Не разрешайте декодеру возвращать ноль после ошибки. Некоторые библиотеки преобразования так делают, если вызывающая сторона не проверяет статус, и превращают поврежденную денежную сумму в допустимую. Возвращайте размеченный результат, который требует обработать допустимое, недопустимое и отложенное состояния. Система типов должна мешать случайной арифметике над недекодированными байтами.
Отрицательный ноль заслуживает отдельного теста. Упакованный или зонный вход может содержать отрицательный знак при нулевых цифрах. Большинство расчетов считают -0.00 и 0.00 равными, но побайтово точный выходной файл, аудиторский поток или ветвление по знаку могут вести себя иначе. Решите, нормализует ли декодирование знак, сохраняет ли отдельный признак или оставляет исходные байты для обратной записи. Отсутствие решения нельзя считать политикой.
Знаковый overpunch использует другой контракт
Знаковый overpunch относится к зонным десятичным полям и числовым данным DISPLAY, а не к COMP-3, хотя оба формата прячут знак в позиции цифры. Если их перепутать, поврежденное значение все равно может выглядеть правдоподобным.
В зонном десятичном EBCDIC каждая цифра занимает байт. При замыкающем overpunch старший полубайт последнего байта задает знак, а младший хранит последнюю цифру. Положительное 123 может заканчиваться байтом с положительной зоной, а -123 использует отрицательную. После преобразования в символы по распространенным таблицам такие байты могут выглядеть буквами или фигурными скобками. Видимая форма относится к кодировке, а не к числовому значению.
Объявление поля и кодировка файла должны рассматриваться вместе. ASCII-парсер не может уверенно понять 12L как минус три без знания таблицы overpunch, которая создала строку. Если преобразовать EBCDIC в Unicode до числового декодирования, можно уничтожить исходные зонные биты или получить символы, которые отвергнет обычный десятичный парсер.
Декодируйте в таком порядке:
- Разрежьте запись по байтовому формату до того, как преобразование символов изменит смещения.
- Примените заданную кодовую страницу EBCDIC к обычным текстовым полям, а числовые байты DISPLAY передайте декодеру зонного десятичного формата.
- Проверьте зону каждой цифры и набор допустимых знаков.
- Верните то же представление из коэффициента и масштаба, что и для упакованного формата.
- Сохраните сырые байты отклоненных записей, чтобы оператор мог найти настоящего производителя.
SIGN IS LEADING, SIGN IS TRAILING и SIGN IS SEPARATE меняют контракт. Отдельный знак занимает собственную символьную позицию, а overpunch нет. Парсер copybook, который сводит все знаковые DISPLAY к одному правилу, сдвигает границы записи или теряет знак.
Допустимо одно объединение: после декодирования упакованные и зонные числа могут использовать общее доменное представление. Граничный декодер у них должен быть разным. Отдельные декодеры не пропускают правила хранения в бизнес-расчеты и выдают точную ошибку вроде invalid packed digit at byte 3 вместо number format error.
Точного десятичного типа недостаточно
Для денег используйте целые коэффициенты, типы с фиксированной точкой или точные числовые типы базы. Никогда не проводите десятичное значение COBOL через двоичную плавающую точку, даже временно в JSON или электронной таблице. Многие десятичные дроби не имеют точного двоичного представления.
Целевой тип должен учитывать наблюдаемые операции и диапазон. S9(7)V99 COMP-3 можно представить знаковым 64-битным коэффициентом с масштабом два только после доказательства, что все промежуточные произведения помещаются. Значения до 31 цифры часто требуют целого произвольной точности. В базе применяйте DECIMAL(p,s) или NUMERIC(p,s) после тестов округления и переполнения. На границах сети и JSON передавайте десятичные строки с явным правилом масштаба, чтобы потребитель не преобразовал их в плавающую точку.
В Go нет встроенного десятичного типа произвольной точности с фиксированной точкой. Центы можно хранить в int64, если полный расчет доказывает допустимый диапазон, либо обернуть math/big.Int как коэффициент с управляемым масштабом. В Rust подойдет проверяемая целочисленная арифметика или десятичная библиотека с проверенными точностью и режимами округления. number в TypeScript использует двоичную плавающую точку; для денег нужны строки, масштабированный bigint или проверенная десятичная реализация. numeric в PostgreSQL точно хранит десятичные значения, но момент уменьшения масштаба по-прежнему определяет приложение.
Доказывайте диапазон с промежуточными значениями, а не только с хранимыми полями. При умножении девятизначной суммы на семизначную ставку до деления или смены масштаба может понадобиться намного больше девяти цифр. COBOL способен держать промежуточный результат с точностью, заданной арифметическими правилами и параметрами компилятора. Целевой int64 может вместить каждый операнд, но переполниться при их умножении.
Разделяйте масштаб хранения и бизнес-единицу. PIC S9(7)V99 часто обозначает валюту в сотых долях, но объявление не называет валюту, не говорит о допустимости долей цента в расчете и не отличает налог, ставку и сумму. Переносите эти значения в доменные типы, когда программа их раскрывает. Money, Rate и Quantity не должны свободно складываться или умножаться лишь потому, что у них есть десятичный коэффициент.
Не принимайте схему базы за первую и единственную спецификацию. Расширенная со временем колонка может принимать то, что поле COBOL не хранит. Более узкая колонка может показать, что интерфейс округлял до записи. Рассматривайте исходные объявления, операторы, форматы записей и ограничения базы как один числовой поток.
Стройте арифметический API вокруг предметной области, а не открывайте везде общий десятичный объект. Сумму можно сложить с суммой в той же единице. Ставка умножается на сумму и дает промежуточное значение большего масштаба. Для распределения нужно правило остатка: деление 10.00 на троих не дает всем одинаковые центы. Такие ограничения проявляют бизнес-правила, которые легко обойти в слишком свободной библиотеке.
Сериализации тоже нужен контракт. Решите, всегда ли масштаб два дает 12.30, допустим ли плюс, запрещена ли экспоненциальная запись и сколько цифр принимает потребитель. Строка JSON предотвращает двоичное преобразование в вашем сервисе, но браузер, преобразователь сообщений или аналитический загрузчик может сделать его позже. Контрактный тест должен пересекать настоящую границу потребителя.
Округление происходит на границе приемника
В COBOL округление привязано к арифметическому оператору и принимающему полю. Совпадения итогового типа недостаточно, если переходы масштаба отличаются. Наличие или отсутствие ROUNDED меняет наблюдаемое поведение.
Рассмотрим расчет ставки:
01 WS-BASE PIC S9(7)V99 COMP-3.
01 WS-RATE PIC S9(2)V9(5) COMP-3.
01 WS-FEE PIC S9(7)V99 COMP-3.
COMPUTE WS-FEE ROUNDED = WS-BASE * WS-RATE
При WS-BASE = 100.00 и WS-RATE = 0.01255 точное произведение равно 1.2550000. Перенос в масштаб два с обычным округлением до ближайшего дает 1.26. Без ROUNDED отбрасываемые позиции усекаются, поэтому сохраняется 1.25. Современная реализация с единым округлением к четному может выдать 1.26 на одних серединах и 1.24 на других, где режим COBOL округлил бы от нуля. Настоящее правило нужно получить из компилятора, диалекта, оператора и тестов.
Не разбрасывайте вызовы round(2) по переведенному бизнес-коду. Опишите смену масштаба как операцию с именованной семантикой:
rescale(value, targetScale, mode)
modes: truncate, nearestAway, nearestEven, floor, ceiling
Затем отметьте каждую границу восстановленного потока, где теряется масштаб. Сюда входят арифметические приемники, MOVE, вызовы с более узкими параметрами, присваивания в базе, поля отчетов и выходные записи. Центы могут исчезнуть в MOVE, хотя сам расчет сохранял четыре дробных позиции.
При усечении важен знак. Усечение -1.259 к нулю дает -1.25, а математический пол дает -1.26. Языки по-разному определяют деление и остаток для отрицательных значений. Проверяйте реализацию, а не полагайтесь на целочисленный прием.
Ошибка размера тоже входит в результат. ON SIZE ERROR может выбрать отдельную ветку, если значение не помещается в приемник. Другой путь способен обрезать старшие цифры или зависеть от поведения компилятора, которое новая система должна отвергать. В записи паритета нужно хранить факт перехода в ветку ошибки, а не только сохраненное число.
У одного арифметического оператора может быть несколько приемников с разными PICTURE. COBOL применяет результат к каждому с учетом его емкости и правила округления. Если один раз посчитать в самом узком типе и скопировать результат, информация потеряется раньше, чем в исходной программе. Сначала считайте с восстановленной промежуточной точностью, затем отдельно меняйте масштаб для каждого приемника.
Валютные правила не всегда требуют двух знаков после точки. Существуют округление наличных, валюты без младшей единицы и расчеты с долями цента, но copybook не выбирает политику. Восстановите ее по операторам, таблицам и результатам. Универсальный Money.round() не подойдет, если разные вызовы ожидают разные ответы.
Промежуточная точность меняет ответ
Порядок вычислений, арифметические параметры компилятора и определения временных полей могут изменить последнюю копейку даже при точных десятичных типах в обеих системах. Точная арифметика не означает неограниченную.
Сравните две формы:
A = roundToCents(BASE * RATE)
B = roundToCents(roundToScale4(BASE * RATE_PART_1) +
roundToScale4(BASE * RATE_PART_2))
Они связаны алгебраически, но численно могут различаться. Исходная программа могла округлять каждый компонент в рабочее поле до сложения. Если новая реализация объединит выражение и округлит один раз, программа изменится. Удаление рабочих полей COBOL до установления паритета регулярно порождает мелкие расхождения, которые трудно искать.
Документация IBM Enterprise COBOL различает ARITH(COMPAT) и ARITH(EXTEND). Первый режим ограничивает десятичные операнды 18 цифрами, второй допускает 31 и меняет емкость промежуточных результатов с фиксированной точкой. IBM также предупреждает, что NUMVAL может давать приближенные значения, а арифметический режим влияет на результат. Поле, которое хранится как десятичное, во время преобразования может пройти через плавающую точку или промежуточное значение иной ширины.
Составьте реестр параметров компилятора и среды для каждого модуля. Один исходный текст с разными параметрами не гарантирует одинаковый исполняемый контракт. Запишите ARITH, NUMPROC, TRUNC, диалект, версию компилятора и значимые параметры среды. Если сведения о сборке потеряны, создайте маленькие программы-пробы и запустите граничные входы совместимым с продуктивной средой компилятором.
Полезная матрица включает положительные и отрицательные середины, максимальные коэффициенты, ноль с каждым допустимым знаком, произведения с дополнительной промежуточной цифрой и деление с периодическим результатом. Сохраняйте входные байты, вывод DISPLAY, байты результата, коды возврата и отметки ветвей. Такие пробы быстрее заканчивают спор о том, что COBOL делает «обычно».
Сохраните порядок вычислений в первой правильной версии. Когда паритет стабилен, упрощайте выражения по одному. Каждое упрощение должно доказывать сохранение результатов на записанном трафике и сгенерированных границах.
Недопустимые числа тоже нужно перенести
Продуктивные файлы часто содержат байты, нарушающие copybook, а старая программа терпит их до операции, которая требует проверки. Новая система, молча очищающая каждое значение, может ошибаться так же, как система, падающая на первой грязной записи.
IBM пишет, что компилятор обычно предполагает соответствие данных PICTURE и USAGE. NUMCHECK(ZON,PAC) добавляет проверки, когда зонные или упакованные поля выступают отправителями. Набор допустимых знаков может зависеть от NUMPROC и настроек установки. Поэтому две программы читают одну запись, но показывают дефект в разных местах: одна сравнивает поле как число, другая копирует содержащую его группу как байты.
Разберем отказ. Входное упакованное поле содержит 12 34 5A: полубайты цифр допустимы, но активная политика отвергает знак. Ночная программа копирует всю группу в архив и завершается, потому что не использует поле как число. Позже расчет на конец месяца применяет его как отправитель, получает исключение или ошибку проверки и посылает запись оператору. Новая система с немедленным декодированием отвергнет запись на входе. Система, принимающая все знаки от A до F, может включить ее в сумму. Обе изменили эксплуатационное поведение.
Нужна доказанная политика совместимости на уровне поля:
- Строгие поля сразу отвергают неверную цифру, заполнение или знак.
- Отложенные поля сохраняют сырые байты и декодируются на той же смысловой границе, что в источнике.
- Известные допустимые отклонения получают явные ветки декодера и именованные фикстуры.
- Отклоненная запись содержит идентификатор, имя поля, байтовое смещение и шестнадцатеричный вход.
Не делайте снисходительность стандартным режимом. Сначала запустите источник с доступной диагностикой в репрезентативной среде, изучите настоящие файлы и найдите каждого производителя. Грязные данные часто указывают на недокументированный интерфейс, а не на странную привычку мейнфрейма.
Это различие влияет и на внедрение. Числовое расхождение на допустимых данных считается дефектом переписывания. Впервые найденная недопустимая запись может быть дефектом исходных данных, производителя или намеренным отличием совместимости. Команде нужны отдельные счетчики и ответственные, иначе панель паритета превратится в спор об одном красном итоге.
Паритет должен сравнивать не только итоги
Стенд паритета должен воспроизводить одинаковые транзакции в источнике и цели, сравнивать поля, ветви, ошибки и сериализованные байты и только потом переходить к итогам пакета. Одинаковая сумма может скрыть две противоположные ошибки.
Записанный продуктивный трафик дает реалистичные сочетания, но редко охватывает числовые границы. Добавьте сгенерированные случаи для каждого контракта:
- ноль, отрицательный ноль, минимум, максимум и единицу за допустимым диапазоном;
- каждую середину при потере масштаба по обе стороны нуля;
- каждый принимаемый и отвергаемый упакованный или overpunch знак;
- неверные цифры, заполнение, короткие записи и ошибки кодировки;
- значения, переполняющиеся только после умножения или выравнивания масштаба.
Для каждого случая сохраняйте конверт сравнения:
{
"case": "fee-negative-half-cent",
"inputHex": "00001000C000125C",
"source": {
"coefficient": "-126",
"scale": 2,
"status": "OK",
"outputHex": "0000126D"
},
"target": {
"coefficient": "-126",
"scale": 2,
"status": "OK",
"outputHex": "0000126D"
}
}
Точная форма может отличаться, но строки для коэффициентов, превышающих безопасный целый диапазон потребителя, выбраны намеренно. Шестнадцатеричные поля показывают разницу в знаках и заполнении. Статус включает исходные ветви вроде ON SIZE ERROR, а не только код завершения процесса.
Если пакет из миллиона записей расходится на копейку, делите сначала по диапазону записей, затем по транзакции, полю и операции. Записывайте немасштабированные операнды, их масштабы, операцию, промежуточную точность и режим смены масштаба. Трассировка с одной строкой expected 19.42, got 19.43 оставляет всю сложную работу человеку в самый неподходящий момент.
Сравнивайте на трех уровнях. Числовой паритет проверяет коэффициенты после выравнивания заявленных масштабов. Поведенческий сравнивает решения, исключения и следующие записи. Побайтовый проверяет фиксированные интерфейсы, которые должны остаться одинаковыми. Не требуйте побайтового совпадения от нового API, где формат можно безопасно изменить, но не ограничивайтесь числами в регулируемой выгрузке, которую потребитель читает по байтовым позициям.
Оформляйте исключения сравнения как код. Если отличаются метки времени, последовательные номера или намеренно измененный формат, нормализуйте только названные поля и проверяйте правило как продуктивную логику. Общее «игнорировать пробелы» может скрыть значимую позицию знака или сдвиг колонки. У каждого исключения должны быть владелец и условие окончания.
Критерии выпуска должны показывать классы расхождений, а не смешанный процент успеха. Ноль необъясненных денежных отличий остается разумным порогом, даже если есть утвержденные изменения интерфейса. Сохраняйте возможность повтора на источнике, пока у каждого расхождения не появятся фикстура, решение и регрессионный тест. Иначе та же копейка вернется после посторонней оптимизации.
CodeHero использует записанный продуктивный трафик в стенде паритета именно поэтому: успешная компиляция перевода не доказывает сохранение десятичного поведения. В больших системах автоматизируйте каталог полей и генерацию трассировок, чтобы рецензенты разбирали расхождения, а не переписывали copybook вручную.
Контракт миграции должен допускать проверку
Готовый числовой контракт должен позволять пройти от любой целевой суммы назад к исходным байтам и вперед через каждую границу округления. Если он существует только в голове последнего специалиста по COBOL, система не готова к замене.
До переключения денежного потока потребуйте следующие материалы:
- У каждого исходного числового поля есть полное имя, PICTURE, USAGE, диапазон байтов, кодировка, точность, масштаб и политика знака.
- Для каждого целевого типа письменно доказан диапазон с учетом промежуточных значений.
- Каждая потеря масштаба называет режим округления или усечения и исходный оператор, который его задает.
- Для недопустимых данных есть фикстуры принятых, отклоненных, отложенных случаев и отрицательного нуля.
- Набор тестов сравнивает числа, поведение и байты там, где важен соответствующий уровень.
Храните контракт рядом с новым кодом и генерируйте автоматически все возможное. Рецензент должен суметь проверить отображение S9(11)V9(6) -> int64 на максимальных операндах и увидеть ответ, а не принять пометку «помещается».
После переключения оставьте отдельные пограничные метрики для ошибок декодирования, переполнений, уменьшений масштаба и нарушений паритета. Не пишите значения счетов и полные записи в общие журналы. Идентификаторов поля и операции, безопасной ссылки на запись и скрытого коэффициента обычно достаточно, чтобы найти сбой и не создать новую проблему с данными.
Требование к деньгам намеренно строгое. Для каждой копейки нужна история: исходные цифры, трактовка знака, масштаб, промежуточные операции и окончательное правило смены масштаба. Когда новая система показывает эту цепочку для сбоя и доказывает ее на всем корпусе, старое представление можно удалить, не потеряв его поведение.
Вопросы
Что означает COMP-3 в COBOL?
COMP-3 означает упакованное десятичное хранение. В большинстве байтов находятся две цифры, последний полубайт отведен под знак, а PICTURE задает точность и масштаб.
Сколько байтов занимает поле COMP-3?
Для n цифр требуется floor(n / 2) + 1 байтов. Дополнительный полубайт содержит знак, а при четном числе цифр остается начальное заполнение, которое тоже следует проверять.
Занимает ли V в PICTURE отдельный байт?
Нет. V обозначает неявную десятичную точку без места в хранилище, поэтому S9(5)V99 хранит семь цифр и знак с масштабом два.
Можно ли перенести деньги COBOL в double или float?
Безопасно сделать это нельзя. Двоичная плавающая точка не представляет точно многие десятичные дроби, и временное преобразование меняет округление, даже если потом число возвращается в десятичный тип.
Знаковый overpunch совпадает с COMP-3?
Нет. Overpunch помещает знак в зонные биты байта цифры DISPLAY, а COMP-3 хранит цифры в полубайтах и отводит последний под знак.
Какой полубайт означает минус в упакованном формате?
D принято считать отрицательным знаком, C обычно означает плюс, а F часто встречается в беззнаковых данных. Допустимый набор зависит от политики компилятора и интерфейса, поскольку в продуктивных данных бывают другие коды.
Округляет ли COBOL денежные значения автоматически?
Единого правила нет. Результат зависит от оператора, принимающего поля и наличия ROUNDED; без этой конструкции потеря масштаба обычно приводит к усечению.
Почему точная десятичная арифметика может отличаться от COBOL?
У точных типов все равно есть точность, порядок вычислений и правила смены масштаба. Замена временного поля, объединение выражений или однократное округление способны изменить последнюю копейку.
Как переносить отрицательный ноль?
Решите, нормализовать ли его, хранить ли признак знака или сохранять исходные байты для обратной записи. Числовое равенство не определяет поведение ветви по знаку и фиксированного выхода.
Что доказывает правильность денежной миграции COBOL?
Прогоните одинаковые входы через обе системы и сравните коэффициенты, масштабы, ветви, ошибки и нужные выходные байты. Добавьте граничные случаи, потому что записанный трафик редко содержит все середины, переполнения, знаки и поврежденные поля.