Rationale for Ada 2005: Exceptions, generics etc
RUSTOPBACKNEXT
ENG |
3. Numerics
@ Although Ada 95 introduced unsigned integer types in the form of modular types, nevertheless, the strong typing rules of Ada have not made it easy to get unsigned and signed integers to work together. The following discussion using Ada 95 is based on that in AI-340. @ Suppose we wish to implement a simulation of a typical machine which has addresses and offsets. @ We make it a generic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: Exceptions, generics etc
@ENGRUSTOPBACKNEXT3. Численные данные
@ Хотя в Аде 95 были введены беззнаковые целые типы в форме модульных типов, тем не менее, строгие правила приведения типов Ады весьма затрудняют взаимодействие знаковых и беззнаковых целых типов. Следующее обсуждение использует Аду 95 основанную на AI 340.
@ Предположим, что мы желаем осуществить моделирование типичной машины, у которой есть адреса и смещения.
@ Мы делаем это настраиваемым пакетом:
|
@ Адреса представлены как целые числа без знака (модульный тип), тогда как смещения - знаковые целые числа. Функция Calc_Address добавляет смещение к базовому адресу и возвращает адрес.
@ Смещение может быть отрицательным.
@ Мы могли бы попробовать написать:
|
@ но это явно незаконно, потому что Base_Add и Offset имеют различные типы.
@ Мы можем попробовать преобразование типа:
|
@ или возможно, так как у Address_Type могло бы быть ограничение,
|
@ но в любом случае преобразование обречено вызвать исключение Constraint_Error, если значение Offset отрицательно.
@ Тогда попытаемся исхитриться:
|
@ но это вызывает исключение Constraint_Error если Offset_Type'Base'Last < Address_Type'Modulus, что нередко случается. Рассмотрим, например, 32-битовую машину с
|
@ в этом случае Address_Type'Modulus равно величине 2 ** 32, что больше чем Offset_Type'Base'Last, которое равно значению 2 ** 31-1.
@ Теперь пробуем сделать явный тест на отрицательное смещение:
|
@ Но если Address_Type'Base'Last < Offset_Type'Last тогда возбуждается исключение Constraint_Error при некоторых значениях Offset. Вряд ли возможно что этот настраиваемый пакет будет работать для всех возможных пар типов.
@ Если мы попытаемся преодолеть это тогда, мы сталкиваемся с проблемами в попытке сравнить эти две величины, так как они имеют различные типы, и преобразование одного к другому может вызвать Constraint_Error еще раз. Одно из решений состоит в том, чтобы использовать больший тип, чтобы сделать тест, но это, не всегда возможно при некоторых обстоятельствах. Мы могли бы конечно обработать Constraint_Error и затем исправить ответ. Безжалостный программист мог бы даже думать о Unchecked_Conversion, но у этого пути есть свои собственные проблемы.
@ И так далее - это утомительный рассказ.
@ Проблема аккуратно преодолена в Аде 2005 введением нового функционального атрибута:
|
@ S'Mod относится к любому модульному подтипу S и возвращает Arg mod S'Modulus Другими словами, она конвертирует значение universal_integer в модульный тип, используя соответствующую математическую ультрасовременную операцию mod. Мы можем тогда счастливо написать:
|
@ и это всегда работает.
@ Следующая тема в области численных данных касается округления. Одна из проблем в проекте любого языка программирования получить правильный баланс между работой и мобильностью. Это особенно очевидно с числовыми типами, где компьютер должен осуществить только грубое приближение к математическим целым и чисел с плавающей запятой. Лучшая работа достигается при использовании типов и операций, которые соответствуют точно аппаратным средствам. С другой стороны, прекрасная мобильность требует типов точно соответствующим особенностями на всем выполнении.
@ Интересный пример этой проблемы возникает с преобразованиями из типа плавающей запятой до целого типа, когда значение плавающего типа на полпути между двумя значениями целого числа.
@ В Аде 83 политика округления на полпути не была определена особо. В Аде 95 установили декретом, что такое округление всегда осуществляется вверх. Так же как это правило для преобразования в типы целого числа, Ада 95 также ввела функциональный атрибут отбрасывающий дробную часть числа. Таким образом, для подтипа S плавающей запятой тип T, который мы имеем
|
@ Она возвращает самую близкую составную величину с округлением вверх.
@ Ада 95 также дает немного больше контроля в пользу статистических приложений, вводя
|
@ Эта функция возвращает самую близкую составную величину и для на полпути выравнивая значение к чётному значению.
@ Однако, есть много приложений, где мы не заботимся об этой величие, где мы, прежде всего, предпочли бы получить быстрый код. Разработчики сообщили о проблемах с элементарными функциями, где используется поиск в таблице для выбора специфическое многочленного расширения. Любой полиномиал сделает точно также находясь в середине некоторого диапазона. Однако, на некоторых популярных аппаратных средствах, таких как Pentium, делая точное округление, требуемое Адой 95 только, напрасно тратится время, и получающаяся функция возможно на 20 % медленнее. Это серьезно в любом сравнении с C.
@ В Аде 2005 эта проблема преодолена введением атрибута:
|
@ Функция не определяет, какая из смежных составных величин возвращается если X лежит на полпути. Отметим, что это не определенное выполнение, а преднамеренно неуказанное. Оно должно препятствовать пользователям строить код зависимым от специфического поведения и, таким образом, написания непортабельного кода.
@ Zerophiles будет рад отметить, что, если S'Signed_Zeros верен и ответ - ноль тогда, у этого есть тот же самый признак как X.
@ Нужно отметить, что Machine_Rounding, как другие функции округления, возвращает значение типа плавающей запятой, а не universal_integer как можно было ожидать. Таким образом, обычно они будут использоваться примерно в следующем контексте
|
@ Реализация однозначно обнаружит этот случай, чтобы произвести быстрый код.
@ Третье усовершенствование основного языка связано с проблемами в области арифметики целых чисел.
@ Эта тема касается немногих людей, но те кто действительно использует её, вероятно чувствуют раздражение состоянием дел.
@ Неприятность с плавающей запятой состоит в том, что оно машино-зависимо, а целые числа это только целые числа. Много прикладных областей использовали некоторую форму масштабирования целых чисел в течение многих десятилетий и Адовская арифметика целых чисел важно в определенных приложениях, где строгий анализ ошибок желателен.
@ Модель фиксированной точки была несколько изменена при переходе от Ады 83 к Аде 95. Одно изменение состояло в том, что понятия образцовых и безопасных чисел были заменены намного более простой моделью, только основанной на умножении маленького числа. Таким образом рассмотрим тип:
|
@ В Аде 83 маленький был определен, чтобы быть наибольшей степенью 2 не больше чем Del, и в этом случае действительно 2.0 ** (-15). Но в Аде 95, маленький может быть выбран реализацией, чтобы быть любой степенью 2 не больше, чем Del обеспечил конечно, что полный диапазон значений покрыт. На обоих языках пункт аспекта может использоваться, чтобы определить маленький, и это не должна быть степень 2. (Помните, что пункты представления теперь известны как пункты аспекта). более далекое изменение достижения вводило в Аде 95 проблем введение операций на типе universal_fixed и преобразование типа.
@ Небольшая проблема в Аде 83 состояла в том, что явное преобразование типа требовалось в местах, где это, возможно, считали весьма ненужным. Предположим у нас есть переменные F, G, H вышеупомянутого типа Frac, затем в Аде 83 мы не могли написать:
|
@ здесь необходимо использовать явное преобразование:
|
@ В Аде 83, умножение было определено между любыми двумя фиксированными типами и приводило к результату типа universal_fixed, и явное преобразование тогда было обязано преобразовывать это в тип Frac.
@ Это явное преобразование, как полагали, было неприятностью, таким образом правило было изменено в Аде 95, было утверждено, что умножение определено только между universal_fixed операндами что давало результат universal_fixed. Неявные преобразования были позволены для обоих операндов, и результат обеспечил, правила решения типа не идентифицировали двусмысленности. Так, так как ожидаемый тип был Frac, и никакая другая интерпретация не была возможна, неявное преобразование было позволено, и так в Аде 95 мы можем просто написать:
|
@ Подобные правила разделяют Аду 83 и Аду 95.
@ Отметим однако что:
|
@ незаконно в Аде 95 из-за существования распространяющейся типа Duration определенным в пакете Standard. Промежуточным результатом мог быть или Frac или Duration. Таким образом, мы должны добавить где-нибудь явное преобразование.
@ Одна из больших вещей об Аде - способность определить Ваши собственные операции. И в Аде 83 много программистов написали свои собственные арифметические операции для чисел с фиксированной точкой. Это могли быть операции насыщения, в которых результату не позволяется переполниться, а только берется предельное значения. Такие операции часто соответствуют поведению некоторого внешнего устройства. Таким образом мы могли бы объявить:
|
@ и подобные функции для суммирования, вычитания, и деления (берущий заботу по делению на нуль и так далее). Это работает прекрасно в Аде 83, и все вычисления могут теперь использовать новые операции, а не предопределенные естественным способом.
@ Отметим однако что:
|
@ теперь неоднозначно в Аде 83, так как и наше собственное новое "*" и предопределенное "*" являются возможными интерпретациями. Однако, если мы просто пишем более естественное:
|
@ тогда нет никакой двусмысленности. Таким образом мы можем программировать в Аде 83 без явного преобразования.
@ Однако, на Аде 95 мы сталкиваемся с проблемой, когда мы вводим свои собственные операции с тех пор
|
@ неоднозначно, потому что и предопределенная операция и наша собственная операция - возможные интерпретации "*" в этом контексте. Есть, не исправляют для этого в Аде 95 за исключением изменения наших собственных операций умножения, чтобы быть процедурами с идентификаторами, такими как mul и div. Это - очень утомительная хозяйственная работа склонная к ошибкам.
@ Было сообщено, что из-за этой трудности много проектов использующих фиксированную точку не двигался от Ады 83 к Аде 95.
@ Эта проблема решена в Аде 2005 изменением имени правила разрешающей способности запретить использование предопределенного умножения (деления) операция, если есть определяемое пользователем примитивная операция умножение (деления) или для типа операнда, если нет явного преобразования результата, или мы пишем Standard."*" (или Standard."/").
@ Это означает это, когда нет никакого преобразования как в:
|
@ тогда предопределенная операция не может примениться, если есть определяемый пользователем примитив "*" для одного из типов операнда. Таким образом, двусмысленность решена. Отметим, что если есть преобразование тогда, это все еще неоднозначно как и в Аде 83.
@ Если у нас обязательно должно быть преобразование тогда, мы можем всегда использовать квалификацию также или только вместо этого. Таким образом, мы можем написать:
|
@ и это будет однозначно использовать нашу собственную операцию.
@ С другой стороны, если мы действительно хотим использовать предопределенную операцию тогда, мы можем всегда писать:
|
@ Другой пример мог бы быть поучительным. Предположим, мы объявляем три типа TL, TA, и TV представляющее длины, области, и величины. Мы используем сантиметры как основную единицу с точностью до 0.1 см вместе с соответствующими непротиворечивыми еденицами и точностями для областей и величин. Мы могли бы объявить:
|
@ Эти типы имеют явное маленькое равное их дельте и таковы, что никакое масштабирование не обязано осуществлять соответствующие операции умножения и деления. Это отсутствие масштабирования не действительно относится к обсуждению ниже, но просто иллюстрирует, почему у нас могло бы быть несколько установленных типов с фиксированной точкой и операций между ними.
@ Отметим, что у всех трех типов есть примитивные определяемые пользователем операции умножения и деления даже при том, что в случае умножения, TV только появляется в результирующем типе. Таким образом, предопределенное умножение или деление с любым из этих типов как операнды можно рассматривать только, если у результата есть преобразование типов.
@ Как следствие, следующее является правильным:
|
@ но следующее - нет, потому что они не соответствуют определяемым пользователем операциям:
|
@ Но если мы настаиваем на том, чтобы умножать две длины вместе тогда, мы можем использовать явное преобразование таким образом:
|
@ и это использует предопределенную операцию.
@ Если мы должны умножить три длины, чтобы получить величину, не сохраняя промежуточную область тогда, мы можем написать:
|
@ и это однозначно, так как нет никаких явных преобразований и, таким образом, единственные соответствующие операции это те которые мы объявили.
@ Интересно сравнить это с соответствующим решением, использующим арифметику с плавающей запятой, где мы должны были бы сделать нежелательное предопределенное преобразование операций как обсуждалось в предыдущей статье.
@ Надеюсь, что читатель не нашел это обсуждение слишком длинным. Хотя арифметика с фиксированной точкой несколько специализированная область, для тех важно, кто находит это полезным, и это хорошо, чтобы знать, что проблемы с Адой 95 были решены.
@ Есть много других усовершенствований в области численных данных, но они касаются приложения Численных данных которые и так будут обсуждаться в более поздней статье.
2010-10-24 00:26:56
. .