Первая публикация 04.09.2017
Types in engineering problems
Введение
«Зарегистрирована партия нового типа.
Личность типа выясняется.»
Старая шутка.
Вот уже 38 лет мне приходится решать разнообразные задачи путем составления соответствующих программ. За все эти годы диапазон таких задач получился весьма большим: от программирования контроллера AT90S2313 до программы автоматического обращения русско-немецкого технического словаря в немецко-русский, и от расчета оптимального сечения стрингера до рисования вида земной поверхности из иллюминатора с орбиты методом обратной трассировки лучей.
Несмотря на разнообразие задач, все их, пожалуй, можно отнести к одному классу, который я условно называю «инженерный», поскольку большей частью они оперируют понятиями научно-технического характера. При решении всех этих задач я не видел пользы в применении таких понятий, как «абстрактный» тип, т.е. назначению некоторого произвольного свойства объектам программы. Обычно эти свойства при трансляции имеют внутреннее представление в виде целых чисел.
Могут возразить, что я не вижу смысла в таких типах потому, что в старых языках (вроде используемого мною PL/1) вообще не было типизации в современном понимании. Нет, понятие типа все-таки было. Например, если в том же PL/1 описать два несовпадающих объекта пусть даже одинакового объема и попытаться присвоить один другому, то при компиляции получится предсказуемое сообщение об ошибке (рис. 1).
Рис. 1. Ошибка несоответствия типов в PL/1 при компиляции.
Тогда за какое же отсутствие типов критиковали старые языки, в том числе PL/1? Например, за возможность присвоения объекта «строка» объекту «число в формате IEEE-754». Ведь в этом случае ошибок при компиляции уже не будет.
Верно, ошибки не будет. Но в моем понимании это вовсе не разные типы. Это всего лишь работа с числовыми значениями в разном представлении. Просто здесь число в виде строки цифр и специальных символов переводится в число же, но в формате IEEE-754. А сам тип объекта остается одним и тем же – числовым. Но в том же PL/1 вполне можно проследить и разницу между «строкой» и «числом».
Например:
Declare (S1, S2) char(*) varying;
S1=S1||S2;
S1=S1+S2;
В первом случае происходит склейка двух строк в одну, а во втором каждая строка сначала преобразуется в числовое значение, затем эти значения складываются и результат снова преобразуется в строку цифр и, возможно, специальных знаков. Правила языка были разработаны таким образом, чтобы преобразование числовых значений из одной формы представления в другую проходило по возможности без потери точности. Непонимание, что транслятор постоянно применяет эти правила и приводит к неверным утверждениям об отсутствии типов в PL/1.
Огорчает, что «абстрактные» типы, обычно представляемые в целых числах, часто создают лишь иллюзию упрощения задачи, не помогая ее решению. Причем внимание к типизации все время возрастает, а польза от их применения, по крайней мере, в «инженерных» задачах — сомнительна. Если тип – целое число, его можно только сравнить с другими типами, а для перечисляемых типов – определить предыдущий/следующий из списка. В учебниках охотно приводят как пример перечисляемого типа «день недели»: понедельник, вторник… Но что это дает, например, для практической задачи определения дня недели по заданной дате? Тип «день недели» никак не помогает, зато помогает формула:
День_недели=([2.6*Месяц-0.2]+День+Год+[Год/4]+[Столетие/4]-2*Столетие) mod 7, где квадратные скобки означают целую часть числа.
В современных языках при решении «инженерной» задачи я легко могу ввести «абстрактные» типы, например, «длина» и «ширина». Но при использовании механизма таких типов, единственное, что при этом получаю по умолчанию – это запрет использования их в одной операции без специального указания на «преобразование» типов. А по физическому смыслу все просто: «длина» и «ширина» — однотипные величины, если умножить «длину» на «ширину» должен получиться тип «площадь». А при делении «длины» на «ширину» должен получиться безразмерный коэффициент, который, как и обычные константы может быть использован в выражениях с любыми другими типами.
Разумеется, можно учитывать все эти правила, переопределяя операции языка, имеющего развитую типизацию. Но у многих ли хватает желания и терпения сделать это?
Даже один из авторов языка Ада Н. Джехани в ретроспективе сожалел, что в этот язык изначально им не была встроена система типов в виде «единиц измерений» [1]. Для класса «инженерных» задач эта идея – использовать как систему типов Международную систему единиц СИ, всегда казалась мне очевидной и потенциально полезной, но почему-то нигде в современных распространенных языках не реализованной, за исключением некоторых возможностей в Matlab. И я решил поэкспериментировать со своим транслятором PL/1 [2] и добавить новую типизацию не в виде абстрактных понятий, а прямо в виде размерностей из базовых величин СИ, и затем на примере реальной задачи посмотреть какие плюсы и минусы это даст программисту, а также оценить сложность доработок транслятора.
Физический смысл типов в инженерных задачах
Приписывая при решении «инженерной» задачи объектам программы не абстрактный, а физический смысл (и используя как тип размерность, т.е. выражение производной величины через первичные), мы сразу добавляем хорошо определенное множество зависимостей для этих объектов, а значит, облегчаем транслятору автоматическую проверку корректности действий и, тем самым, повышаем надежность программы.
При этом теоретическое обоснование системы независимых «физических» типов уже давно имеется в виде успешно применяемой теории размерности, основная теорема («пи-теорема») которой формулируется следующим образом [3]: соотношение, не зависящее от выбора единиц измерения между (n+1) размерными величинами a, a1, a2,…an, k из которых имеют независимые размерности, может быть представлено в виде соотношения между (n+1-k) величинами П, П1, П2,… Пn-k, представляющими собой безразмерные комбинации (n+1) размерных величин.
Основной смысл пи-теоремы состоит в том, что всякое физическое соотношение между размерными величинами можно сформулировать как соотношение между безразмерными величинами.
Следовательно, поскольку в СИ независимыми являются лишь семь физических величин:
— длина L (единица — метр),
— масса M (единица — килограмм),
— время T (единица — секунда),
— термодинамическая температура Θ (единица — Кельвин),
— сила электрического тока I (единица — ампер),
— сила света J (единица — кандела),
— количество вещества N (единица — моль),
то в программе любой переменной, которой мы приписали физический смысл, может быть сопоставлен «физический» же тип в виде размерности из степенного одночлена, выраженного через основные единицы: [x]=Ll Mm Tt Θθ Ii Jj Nn.
Таким образом, в программе переменным может быть назначено произвольное число видов «физического» типа, каждый из которых представляется при трансляции одинаково – степенным одночленом, имеющим возможно разные показатели степеней, в том числе нулевые. Если все показатели степеней нулевые – объект не имеет размерности.
Масштабный коэффициент в «физических» типах
На практике величины, имеющие физический смысл, часто задаются в производных единицах измерения, отличающихся от базовых единиц масштабным коэффициентом. Например, длины — в км, площади — в Га и т.д. Поэтому целесообразно ввести в представление «физических» типов еще и масштабный коэффициент в виде одного числа в формате IEEE-754.
Наличие такого коэффициента позволит транслятору автоматически учитывать масштабы при генерировании операций для выражений. Например, если переменным X1, X2, X3 приписаны типы соответственно [км], [см] и [мм], то при вычислении выражения X1=X2+X3, транслятор должен автоматически добавить операции умножения X2 на 0.01 и X3 на 0.001, а затем результат сложения перед присваиванием X1 еще разделить на 1000.
Наличие приближенного масштабного коэффициента дает важное следствие – «физические» типы могут быть тоже только у приближенных переменных, т.е. только у переменных в формате с «плавающей» запятой. Однако для класса «инженерных» задач такое ограничение на переменные практически несущественно.
При этом автоматическая подстановка масштабных коэффициентов при трансляции выражений означает, что все расчеты в программе будут производиться в базовых величинах СИ, как в приведенном выше примере – в метрах. Это также означает, что масштабные коэффициенты в «физическом» типе могут быть только у переменных, но не у выражений, где масштабы уже учтены при генерации операций загрузки переменных этого выражения.
Дополнительные величины в «физических» типах
Хотя в СИ имеется лишь семь базовых величин, на практике используются еще две вспомогательные величины – плоский угол (единица — радиан) и телесный угол (единица — стерадиан).
Здесь наблюдается некоторое противоречие с точки зрения формального определения типа. В самом деле, радиан и стерадиан – по определению безразмерные величины и могут быть использованы или не использованы в выражениях для других производных единиц СИ. Однако в некоторых случаях они ведут себя как обычные (размерные) величины. Например, в PL/1 имеется встроенная функция sin, входным параметром которой должно быть выражение в радианах и функция sind, входным параметром которой должно быть выражение в градусах. Переменная с «физическим» типом «градус» отличается от переменной с «физическим» типом «радиан», масштабным коэффициентом π/180.
Таким образом, целесообразно и радианы и стерадианы также включать в степенной одночлен представления «физического» типа и, например, проверять, что функция sin берется от величины в радианах или в градусах, а не в метрах. Но тогда формально возникает конфликт размерностей при других вычислениях, например:
W=φ/t; // угловая скорость (угол делится на время)
V=R*W; // линейная скорость (радиус поворота умножается на угловую скорость)
В самом деле, если угол φ имеет «физический» тип «радиан», то получается, что угловая скорость W имеет «физический» тип «радиан в секунду», а линейная скорость после умножения на радиус – «метр на радиан в секунду», что не соответствует требуемой размерности скорости «метр в секунду».
Поэтому хотя вспомогательные величины радиан и стерадиан действительно также представлены в степенном одночлене «физического» типа, но ведут себя не совсем так, как семь остальных базовых величин. Они считаются «размерными» величинами, только если остальные величины в одночлене имеют нулевые показатели степени (т.е. отсутствуют). Как только при вычислениях появляется любая другая величина – радиан и стерадиан «сокращаются», т.е. их показатель степени транслятором обнуляется. Тогда в приведенном примере выражение φ/t (и угловая скорость W) получает «физический» тип «единица, деленная на секунду», а выражение R*W правильный «физический» тип скорости «метр в секунду». Но при этом выражение, например, sin(2*φ) по-прежнему можно проверить на корректный «физический» тип аргумента «радиан».
Внутреннее представление «физического» типа в трансляторе
Исходя из приведенных выше теоретических и практических соображений, каждой переменной программы, которой может быть приписан некоторый физический смысл, программистом может быть присвоен универсальный «физический» тип из величин СИ. Данный тип в таблице транслятора PL/1 представлен в виде одного масштабного коэффициента в формате IEEE-754 и занимающего восемь байт, а также девяти показателей степени основных и вспомогательных величин в смысле единиц СИ.
Поскольку вычисления в «инженерных» задачах проводятся и с дробными степенями, например, Z=SQRT(X**2+Y**2), то показатели степени в универсальном «физическом» типе представлены рациональными (и возможно неправильными) дробями. При этом байт занимает числитель и байт знаменатель. Таким образом, показатели степени в трансляторе могут быть представлены числами от -128 до 127, а в целом каждый «физический» тип переменной занимает в таблице транслятора 8+9*2=26 байт.
Действия с «физическими» типами при вычислении выражений
При анализе выражений в программе и генерации операций над переменными с «физическими» типами транслятор производит вполне очевидные действия, чтобы получить текущий «физический» тип всего выражения.
При умножении переменных показатели степеней их «физических» типов складываются. Поскольку степени представлены дробями, то и складываются они, естественно, как дроби.
При делении переменных показатели степеней их «физических» типов вычитаются.
При возведении переменной в степень-константу показатели степеней «физического» типа умножаются на эту константу. При этом возведение в нецелую степень или в степень-переменную для «физического» типа считается ошибкой, за исключением случая представления степени-константы в виде дроби. Например, в выражении X**(1e0/3e0) показатели степени «физического» типа переменной X будут умножены на дробь 1/3, т.е. в данном случае знаменатели степеней будут умножены на три.
Естественно, что после каждого из перечисленных выше действий транслятором проводится попытка приведения дробей показателей степеней к правильному виду. И для удобства работы проверяется частный случай сокращения всех базовых величин, т.е. случая получения всех нулевых числителей показателей степеней. Тогда и весь «физический» тип просто обнуляется, выражение «теряет» тип и становится «чисто» безразмерным, что упрощает и ускоряет последующие операции с типами.
При сложении, вычитании, сравнении и присваивании переменных с «физическими» типами, сами типы, естественно, не меняются, а просто сравниваются на совпадение. При этом масштабные коэффициенты учитываются в выражениях и присваиваниях и поэтому в этих операциях могут не совпадать. Однако есть частные случаи присваивания или сравнения (например, векторов), когда присваивание или сравнение реализуется просто побайтным переписыванием или сравнением двух участков памяти. В таких случаях и масштабные коэффициенты «физических» типов у сравниваемых или присваиваемых переменных обязаны также совпадать.
Задание «физических» типов в исходном тексте программы
При доработке транслятора для удобства задания «физических» типов с сохранением возможности трансляции уже написанных программ (без таких типов), были использованы символы квадратных скобок, ранее вообще не применявшиеся в языке PL/1, например:
Declare V float(53) [м/с];
Внутри квадратных скобок могут быть записаны произвольные выражения, состоящие из скобок, знаков умножения, деления, возведения в степень, числовых констант и девяти имен базовых и вспомогательных величин СИ: м, кг, с, а, к, моль, кд, рад, ср. Для угловых величин допустимо также имя «ПИ».
Например:
Declare Mu float(53) [(1000*m)**3/c**2];
Declare Fi float(53) [пи/180*рад];
Встроенная таблица «физических» типов в трансляторе имеет лишь девять первых заполненных строк перечисленными выше базовыми величинами, однако с помощью препроцессорного оператора замены %replace в нее можно добавлять произвольное число «нестандартных» типов, например:
%Replace
[км] by [1000*м],
[час] by [3600*c],
[миля] by [1852*м],
[узел] by [миля/час];
…
Declare
скорость float(53) [км/час],
скорость_судна float(53) [узел];
При этом после выполнения оператора %Replace имена «км», «час», «миля» и «узел» становятся допустимыми только внутри квадратных скобок описания «физического» типа, но не в остальном тексте программы.
Присваивание переменным с «физическими» типами начальных значений
При обработке оператора присваивания переменной с «физическим» типом транслятор проверяет, что показатели степени девяти величин СИ в представлении типа в левой и правой части присваивания совпадают. Но как быть при присваивании переменной начального значения не выражения, а, например, константы?
Вроде бы напрашивается непосредственное указание «физического» типа у констант прямо в операторах программы, например:
V=10 [км/час];
Но мне такой подход не кажется логичным. Все-таки числовые константы, записываемые непосредственно в физических уравнениях не должны иметь собственной размерности. Это удел переменных, даже, если они и не меняют в данном конкретном случае своих величин. Поэтому инициализация значений в доработанном трансляторе выполняется так:
Declare
// константы
g float(53) static init(9.81e0) [м/c**2],
v0 float(53) static init(10e0) [км/час],
// переменные
m float(53) [кг],
v float(53) [км/час],
F float(53) [кг*м/с**2];
…
v=v0; F=g*m;
Исключение составляет константа 0. Ее «физический» тип может быть любым:
v=0; F,m=0;
Чтение и запись переменных с «физическими» типами
В файловом обмене языка PL/1 существует два вида чтения/записи объектов: двоичный и текстовый, реализуемые соответственно операторами read/write и get/put.
Естественно, что двоичный обмен операторами read/write осуществляется без каких-либо преобразований и поэтому переменные с «физическими» типами записываются и читаются из файлов точно так же как и любые другие переменные.
А вот в другом случае появляется несколько непривычное свойство вывода переменных с «физическими» типами. Поскольку в общем случае текстового вывода выдаваемый объект – это выражение, то для переменных с «физическими» типами в нем автоматически будут учтены масштабные коэффициенты, а, следовательно, выражение всегда будет выведено в базовых единицах СИ, например, в метрах, даже когда выводится одна переменная и она задана в километрах или в миллиметрах.
Для того чтобы операторы put и get оставались «зеркальными» транслятор автоматически учитывает масштабный коэффициент переменной и при чтении ее оператором get, т.е. при чтении также подразумевается, что значения хранятся в файлах в базовых единицах СИ.
Если все же необходим текстовый обмен без учета масштабных коэффициентов, то в PL/1 это можно реализовать, используя т.н. «наложенные» переменные, т.е. описав переменные без «физических» типов по тем же адресам памяти, что и переменные с «физическими» типами, например, в случае:
Declare
X1 float(53) static init(10e0) [км],
X2 float(53) defined(X1);
put skip list(X1,X2);
будут выданы значения 10000 и 10.
Встроенная переменная ?TYPE для выражений с «физическими» типами
При добавлении в транслятор «физических» типов традиционно следовало бы добавить и встроенную функцию, например, с именем TYPE, возвращающую «физический» тип своего аргумента. Однако я решил вместо этого ввести встроенную переменную (строку) ?TYPE, автоматически принимающую значение «физического» типа (т.е. размерности) последнего вычисленного выражения.
В самом деле, вычислять «физический» тип для одной переменной с помощью встроенной функции довольно бессмысленно, поскольку эта же переменная описана с этим же «физическим» типом здесь же, в исходном тексте. «Физический» тип обычно нужен при выводе результатов в виде выражений, но тогда выражение транслятору придется вычислять два раза, например:
put skip list(s/t,type(s/t));
Поэтому для вывода удобнее использовать встроенную переменную, а не функцию, например при выполнении оператора вывода в данном примере:
Declare
s float(53) static init(10) [км],
t float(53) static init(5) [час];
…
put skip list(s,?type,t,?type,s/t,?type);
будут выданы следующие значения (рис. 2):
Рис. 2. Использование встроенной переменной ?TYPE при выводе результатов
«Физические» типы формальных параметров подпрограмм
Формальные параметры подпрограмм, как и обычные переменные, естественно, также могут иметь «физический» тип с масштабным коэффициентом. При вычислении и подстановке фактического параметра транслятор выполняет те же действия, что и при обычном присваивании, т.е. сравнивает показатели степеней базовых величин и умножает на масштабный коэффициент.
Однако если параметром является не скалярная величина, а сразу агрегат данных, масштабный коэффициент формального и фактического параметров должны строго совпадать и в этом случае на коэффициент, конечно, ничего не умножается.
Также, разумеется, «физический» тип может иметь возвращаемое подпрограммой значение:
Declare
//модуль радиус-вектора
Vmod entry((3) float(53)[км]) returns(float(53)[км]);
Поскольку на практике используются и подпрограммы с полиморфными типами, я допустил задание в формальных параметрах неопределенного «физического» типа (с помощью знака «?»), при котором проверки на совпадение и учет масштабных коэффициентов отключаются.
Declare
//модуль любого вектора
Vmod0 entry((3) float(53)[?]) returns(float(53)[?]);
Еще один особый случай представляет встроенная функция квадратного корня SQRT. Она, разумеется, тоже имеет неопределенный «физический» тип формального аргумента, но возвращает всегда такой же тип как у фактического аргумента и в степени ½.
К сожалению, это правило нельзя распространить на любые подпрограммы с неопределенными типами, поскольку в общем случае они могут возвращать «физические» типы не обязательно совпадающие с типом входного аргумента.
Отладочные средства для «физических» типов
Кроме встроенной переменной ?TYPE, есть еще два отладочных средства. Это возможность вывести всю таблицу переменных программы, включая теперь и их «физические» типы с масштабными коэффициентами и собственно сообщения при компиляции о несоответствии типов в вычисляемых выражениях (рис. 3). В данном случае в небольшом тесте, приведенном ниже, в операторе сравнения «физические» типы справа и слева оказались не равны.
test:proc main;
%replace
[км] BY [1000*м],
[час] BY [3600*с];
declare
s float(53) static init(10) [км],
t float(53) static init(5) [час],
v float(53) [км/час];
if s*t>v*t then stop;
…
Рис. 3. Вывод транслятором таблицы переменных с «физическими» типами и сообщения о несоответствии «физических» типов в операции сравнения.
Пример применения «физических» типов
Для оценки удобства применения «физических» типов была взята программа расчета орбитальных параметров, написанная и, разумеется, тщательно отлаженная еще много лет назад. Поскольку программа уже была создана, требовалось лишь «восстановить» физический смысл ее величин, учесть в некоторых случаях масштабные коэффициенты и убедиться, что с точки зрения размерностей «физических» типов все уравнения записаны корректно.
Это удалось сделать, т.е. удалось после изменений исходного текста получить такие же числовые результаты и без вывода сообщений транслятора о несоответствии типов.
Примеры изменений исходного текста данной программы приведены ниже.
а) Потребовалось ввести ряд переменных с типами вместо ранее безразмерных констант, например:
// СКОРОСТЬ ВРАЩЕНИЯ ЗЕМЛИ
WEarth float(53) static init(0.000072921158e0) [РАД/C],
// ДВА ПИ
Dpi float(53) static init(6.28318530717958648e0) [РАД],
// Epsilon/Mu
Em float(53) static init(66072.1866e0) [KM**2],
// ГРАВИТАЦИОННЫЙ ПОТЕНЦИАЛ
Mu float(53) static init(398600.4e0) [KM**3/C**2],
// ПОЛУОСЬ ДЛЯ СРЕДНЕЙ ВЫСОТЫ 358 КМ
Am float(53) static init(6736e0) [KM],
//ЭКВАТОРИАЛЬНЫЙ РАДИУС ЗЕМЛИ
Re float(53) static init(6378.137e0) [KM],
б) Потребовалось убрать ряд приведений масштабов переменных, например:
Было:
//—- ПРИВЕДЕНИЕ МАСШТАБОВ К КМ И +-180 —-
do i=1 to 3;
vsg(i)=vsg0(i)/1000e0; // ПЕРЕВЕЛИ В КМ
vrg(i)=vrg0(i)/1000e0; // ПЕРЕВЕЛИ В КМ
if Lamseans(i) > 180e0 then Lamseans(i)-=360e0;
end i;
Стало:
//—- ПРИВЕДЕНИЕ МАСШТАБОВ К КМ И +-180 —-
do i=1 to 3;
vsg(i)=vsg0(i); // АВТОМАТИЧЕСКИЙ ПЕРЕВОД В КМ
vrg(i)=vrg0(i); // АВТОМАТИЧЕСКИЙ ПЕРЕВОД В КМ
if Lamseans(i) > У_180 then Lamseans(i)-=У_360;
end i;
Было:
BetaBal=60e0*(per2-per1)/(te2s-te1s)/2e0; // ПЕРИОД БЫЛ В МИНУТАХ
Стало:
BetaBal=(per2-per1)/(te2s-te1s)/2e0; // ПЕРИОД БЫЛ В МИНУТАХ
в) Некоторые произвольно разбитые на части формулы потребовалось свернуть, чтобы не вводить промежуточных переменных с нестандартной размерностью, например:
Было:
//—- ТЕКУЩИЙ РАДИУС ДЛЯ РАСЧЕТА ВЫСОТЫ —-
Radius = J3-et1+dz1*dz1+J4*cos(ArgLat+ArgLat);
Radius *= Axe*(1e0+J7*tp);
Стало:
//—- ТЕКУЩИЙ РАДИУС ДЛЯ РАСЧЕТА ВЫСОТЫ —-
Radius = Axe*(1e0+J7*tp)* (J3-et1+dz1*dz1+J4*cos(ArgLat+ArgLat));
Однако большей частью исходный текст остался без изменений. В результате тестовых расчетов были получены те же результаты, что и у исходной программы, при трансляции сообщений о несоответствии типов не выдавалось, например, самая громоздкая из использованных формул дала корректную размерность длины:
//—- ОЦЕНКА ПОЛУОСИ ПО ЗНАЧЕНИЮ ПЕРИОДА —-
do AxeOfPer=Am, i=1 to 4; // НАЧИНАЕМ С ВЫСОТЫ 358 КМ
AxeOfPer=((Period/Dpi)**2*Mu)**(1e0/3e0)
/(1e0-2e0/3e0*Em/AxeOfPer/AxeOfPer*(4e0*CosIncl*CosIncl-1e0));
end;
Однако одна логическая ловушка с точки зрения размерностей в программе все-таки нашлась:
//—- ШИРОТА В ГРАДУСАХ С УЧЕТОМ ЭЛЛИПСОИДНОСТИ ЗЕМЛИ —-
Fi = Fi + Alz*sin(2e0*Fi);
Здесь Alz – безразмерный коэффициент сжатия земного эллипсоида, равный 1/298.257. Получается, что в формуле угол Fi в радианах складывается с заведомо безразмерным синусом, т.е. несоответствие размерности. Так сказалось формальное представление углов условно размерной величиной «радиан», несмотря на то, что реальный радиан величина безразмерная. Для формального же преодоления этого несоответствия в формуле пришлось дополнительно умножить синус на переменную с «физическим» типом «радиан» и единичным значением.
Заключение
Встраивание в транслятор языка PL/1 дополнительной типизации и назначение нового свойства переменным, определенного как «физические» типы в виде базовых и вспомогательных единиц Международной системы СИ позволило:
— автоматически проверять по размерности корректность записи уравнений, имеющих физический смысл;
— автоматически учитывать различные несистемные единицы измерений, при этом организовать сами вычисления всегда в семи базовых и двух вспомогательных единицах СИ.
Если внутреннее представление «абстрактного» типа – целое число, то с таким типом можно выполнять лишь простейшие действия, например, сравнивать на совпадение или определять предыдущий/следующий тип. Обычно автоматически при вычислениях такой тип не меняется.
В отличие от «абстрактных» типов, которых может быть бесконечное число и отношения между которыми должен задавать программист, число «физических» типов мало и они независимы друг от друга по определению.
Кроме этого «физический» тип изначально хранит гораздо больше информации об объекте и автоматически меняется при вычислении выражений по вполне очевидным правилам. Масштабный коэффициент «физического» типа позволяет легко оперировать внесистемными единицами измерений и при этом автоматически сводить все вычисления к системным единицам. На практике, пользоваться в «инженерных» задачах «физическими» типами оказалось вполне удобно и естественно. Объем доработок транслятора оказался сравнительно небольшим.
Несмотря на то, что некоторые действия в программе из-за новых типов упрощаются, а некоторые встроенные функции (например, упомянутая выше sind) стали ненужными, в целом число операций в вычислениях может увеличиться (из-за добавления умножений на масштабные коэффициенты). Однако при этом проверки корректности программы облегчаются и ее надежность возрастает.
Как пример важности таких проверок корректности можно привести неудачу миссии Mars Climate Orbiter 23 сентября 1999 года из-за расчетов в «не тех» единицах измерения. Вполне вероятно, что если бы используемые трансляторы тогда имели бы механизм «физических» типов, подобный приведенному выше, а также автоматический вывод в системных единицах, можно было бы выполнять расчеты в злополучных «фунт-силах», но при этом избежать последующих разночтений с печальными последствиями.
Литература
- Языки программирования Ада, Си, Паскаль. Сравнение и оценка. Под редакцией А. Фьюера, Н. Джехани. Радио и связь, 1989, с. 182.
- Д.Ю.Караваев «К вопросу о совершенствовании языка программирования» RSDN Magazine #4 2011, с. 15-21.
- А.С Романов, А.В. Семиколенов, С.Н. Тараненко, А.П. Шахорин Теория подобия и размерности. Пограничный слой. Электронное учебное издание МГТУ им. Н.Э. Баумана, 2011, с. 8.