![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Rational Number LibraryBoost , ,
|
gcd(n, m) | Самый большой общий делитель n и m | ||
lcm(n, m) | Наименее общее кратное n и m |
Эти шаблоны функций теперь пересылают вызовы своим эквивалентам вBoost. Целая библиотека. Их присутствие можно контролировать во время компиляции с постоянной препроцессора<BOOST_CONTROL_RATIONAL_HAS_GCD
>.
Рационалы могут быть построены из нуля, одного или двух целочисленных аргументов; представляющих конструкцию по умолчанию как ноль, преобразование из целого числа, представляющего собой числитель с неявным знаменателем одного или пары числителя и знаменателя в этом порядке соответственно. Целый аргумент должен быть целочисленного типа рационального или неявно конвертируемым в этот тип. (Для конструктора с двумя аргументами любые необходимые преобразования оцениваются независимо.) Компоненты хранятся в нормализованном виде.
Рационалы также могут быть построены из другого рационального. Когда источник и пункт назначения, лежащие в основе целых типов чисел, совпадают, используется автоматически определенный конструктор копий или движений. В противном случае используется шаблон конвертирующего конструктора. Конструктор выполняет инициализацию числителя и знаменателя по принципу члена. Преобразования на уровне компонентов, которые отмечены<explicit
>, хороши. Когда конверсия заканчивается сохранением ценности, она уже нормализуется; но проверка на нормализацию выполняется в случае нарушения сохранения ценности.
Это означает, что следующие утверждения действительны:<
I n, d; rational<I> zero; rational<I> r1(n); rational<I> r2(n, d); rational<J> r3(r2); // assuming J(n) and J(d) are well-formed>
Конструктор без аргументов, конструктор с одним аргументом и шаблон конструктора с кросс-версией помечены как<constexpr
>, что делает их жизнеспособными в постоянных выражениях, когда инициализаторы (если таковые имеются) также являются постоянными выражениями (и необходимые операции из базового целого типа(ов)<constexpr
>-включены).
Конструктор единого аргументанеобъявлен явным, поэтому существует неявное преобразование из базового целого типа в рациональный тип. Конструктор с двумя аргументами можно считать неявным преобразованием с однородным синтаксисом инициализации C++11, поскольку он также не объявлен явным. Шаблон конструктора кросс-версии объявлен явным, поэтому необходимо указать направление преобразования между двумя рациональными инстанциациями.
+ += - -= * *= / /= ++ -- (both prefix and postfix) == != < > <= >= Unary: + - !>
До сих пор только<operator ==
>, unary<operator +
>и<operator !
>являются<constexpr
>.
Функция бросит, если данные компоненты не могут быть сформированы в действительное рациональное число. В противном случае он может бросать только в том случае, если может бросать назначение движения на уровне компонента (в C++11; копирование для более ранних версий C++). Сильная гарантия сохраняется, если бросок происходит в первой части, но существует риск того, что ни сильная, ни базовая гарантия не произойдет, если исключение будет брошено во время заданий компонентов.
Существует оператор преобразования в неопределенный булевый тип (скорее всего, указатель участника). Этот оператор преобразует рациональное в<false
>, если оно представляет собой ноль, и<true
>в противном случае. Это преобразование позволяет рационально использовать в качестве первого аргумента оператора<?:
>; в качестве либо аргумента операторов<&&
>или<||
>без потери оценки короткого замыкания; в качестве условия для утверждения<do
>,<if
>,<while
>или<for
>; и в качестве условного заявления для утверждений<if
>,<while
>или<for
>. Природа используемого типа и то, что любые имена для этой природы хранятся в тайне, должны предотвращать любое ненадлежащее небулевое использование, такое как числовые или указательные операции или как<switch
>условие.
Нетдругихнеявных превращений из рационального типа. Помимо явного шаблона конструктора кросс-версии, существует явная функция преобразования типаrational_cast
rational<int> r(22,7); double nearly_pi = boost::rational_cast<double>(r);>
Поведение функцииrational_cast
По существу, все требуемые преобразования должны сохранять ценность, и все операции должны вести себя «разумно». Если эти ограничения не могут быть выполнены, будет более целесообразным отдельное пользовательское преобразование.
Булева конверсия ирациональное_cast<constexpr
>-включено.
Записка об осуществлении:
Реализация функции рационального кастинга<
template <typename Float, typename Int> Float rational_cast(const rational<Int>& src) { return static_cast<Float>(src.numerator()) / src.denominator(); }>Программы не должны быть написаны, чтобы зависеть от этой реализации, тем более, что эта реализация в настоящее время устарела. (Требуется смешанное разделение типовFloatиInt, в отличие отInteger Type Requirements.)
constexpr
>.Эти функции позволяют коду пользователя реализовать любую дополнительную требуемую функциональность. В частности, следует отметить, что могут быть случаи, когда вышеупомянутая операция рационального броска неуместна, особенно в тех случаях, когда рациональный тип основан на неограниченном целочисленном типе. В этом случае более подходящим будет специально написанное пользовательское преобразование в плавающую точку.
Не будет предпринято никаких попыток предоставить подробные гарантии эффективности операций, доступных на рациональном классе. Хотя такие гарантии могут быть предоставлены (подобно спецификациям производительности многих стандартных библиотечных классов), ни в коем случае не ясно, что такие гарантии будут иметь значительную ценность для пользователей рационального класса. Вместо этого в этом разделе будет представлено общее обсуждение характеристик рационального класса.
Ниже приводится перечень основных операций, определенных в заголовке
Обратите внимание, что подразумевается, что операции на IntType имеют «обычные» эксплуатационные характеристики — в частности, что дорогостоящие операции являются умножением, делением и модулем, при этом сложение и вычитание значительно дешевле. Предполагается, что строительство (от целых букв 0 и 1, а также копирование строительства) и присвоение относительно дешевы, хотя некоторые усилия предпринимаются для уменьшения ненужного строительства и копирования. Также предполагается, что сравнение (особенно с нулем) дешево.
Целые типы, не соответствующие этим предположениям, не будут особенно эффективны в качестве основного целого типа для рационального класса. В частности, вполне вероятно, что производительность будет крайне неоптимальной.
Кроме того, если операции на базовом целочисленном типе могут генерировать исключения, они будут распространяться из операций на рациональный класс. Никаких особых предположений делать не следует — можно только предположить, что любые исключения, которые могут быть брошены целым классом, могут быть брошены любой рациональной операцией. В частности, рациональный конструктор может отбрасывать исключения из базового целого типа в результате этапа нормализации. Единственным исключением из этого правила является то, что рациональный деструктор будет бросать только исключения, которые могут быть брошены деструктором основного целого типа (обычно нет).
Если оператор(ы) назначения уровня компонента может (могут) бросить, то инварианты рационального объекта могут быть нарушены, если во время назначения второго компонента происходит исключение. (Здесь также указана функция<assign
>). Это нарушает как сильные, так и базовые гарантии.
Внутри рациональные числа хранятся как пара (числитель, знаменатель) целых чисел (тип которых указан как шаблонный параметр для рационального типа). Рационалы всегда хранятся в полностью нормализованном виде (т.е. gcd (числитель, знаменатель) = 1, а знаменатель всегда положительный).
Области, в которых это минимальное соображение было смягчено, обеспечивают операторов ввода/вывода и rational_cast. Первый, как правило, бесспорен. Тем не менее, существует ряд случаев, когда рациональное преобразование не является наилучшим способом для преобразования рационального в значение с плавающей точкой (в частности, когда задействованы определенные пользователем типы). В этих случаях может и должно быть реализовано определенное пользователем преобразование. Нет необходимости называть такую операцию рациональной_cast, и поэтому функция рациональной_castнеобеспечивает необходимую инфраструктуру для специализации/перегрузки.
К сожалению, стандарт C++ не предлагает такого класса(и в настоящее время не повышает). Поэтому вполне вероятно, что класс рациональных чисел во многих случаях будет использоваться с ограниченно точными целыми типами, такими как встроенныйintтип.
При использовании с ограниченным целым точным типом рациональный класс страдает от многих проблем точности, которые вызывают трудности с типами с плавающей точкой. Хотя вполне вероятно, что проблемы точности не повлияют на простое использование рационального класса, пользователи должны знать, что такие проблемы существуют.
В качестве простой иллюстрации проблем, связанных с ограниченными целыми числами точности, рассмотрим случай, когда тип C++intпредставляет собой 32-битное подписанное представление. В этом случае наименьший возможный положительный рациональный Целые типы с ограниченной точностью могут вызывать проблемы с размерами диапазона допустимых отрицательных и положительных значений. Если отрицательный диапазон больше, то крайне отрицательные числа не будут иметь обратной добавки в положительном диапазоне, что делает их непригодными для использования в качестве значений знаменателя, поскольку они не могут быть нормализованы до положительных значений (если пользователю не повезет, что входные компоненты не являются относительно простой предварительной нормализацией). Пользователю рационального типа, основанного на целочисленном типе с ограниченной точностью, необходимо знать и кодировать в ожидании таких проблем. Ключевой проблемой любой функции преобразования из значения с плавающей точкой является то, как справиться с потерей точности, которая участвует в операциях с плавающей точкой. Чтобы привести конкретный пример, рассмотрим следующий код:< Фундаментальный вопрос заключается в том, что именно должно быть рациональным? Наивный ответ заключается в том, что r должно быть равно 1/3. Однако это игнорирует множество вопросов. В первом случае z не является точно 1/3. Из-за ограничений представления с плавающей точкой 1/3 не может быть точно представлено ни в одном из общих представлений для двойного типа. Следовательно, не должен ли r содержать (точное) представление фактической величины, представленной z? Но будет ли пользователь доволен значением 333333333333331/100000000000000000000 для r? Прежде чем даже рассматривать вышеупомянутую проблему, мы должны рассмотреть точность исходных значений, x и y. Например, если они получены из аналогового измерительного прибора, они не являются бесконечно точными. В таком случае рациональное представление, подобное вышеизложенному, обещает гораздо большую точность, чем есть какое-либо оправдание. Все это означает, что мы должны искать какую-то форму «ближайшей простой фракции». Алгоритмы для определения такого рода значений существуют. Однако не все приложения хотят работать так. В других случаях весь смысл преобразования в рациональное состоит в том, чтобы получить точное представление, чтобы предотвратить потерю точности во время ряда вычислений. При этом требуется совершенно точное представление, независимо от того, как выглядят «неестественные» фракции. С этими противоречивыми требованиями явно нет единого решения, которое удовлетворило бы всех пользователей. Кроме того, используемые алгоритмы являются относительно сложными и специализированными и лучше всего реализуются с хорошим пониманием требований приложения. Все эти факторы делают такую функцию непригодной для библиотеки общего назначения. Первая проблема заключается в том, что для того, чтобы найти подходящую реализацию abs (IntType) в случае, когда IntType является определяемым пользователем типом в пространстве имен пользователя, требуется поиск Koenig. Не все компиляторы поддерживают поиск функций Koenig в настоящее время. Для таких компиляторов требуются неуклюжие обходные пути, требующие сотрудничества с пользователем рационального класса. Вторая и потенциально более серьезная проблема заключается в том, что для нестандартных встроенных целых типов (например, 64-битных целых типов, таких какдлинноеили__int64) нет никакой гарантии, что поставщик поставил встроенную функцию abs(), работающую на таких типах. Это проблема качества реализации, но с практической точки зрения поддержка поставщиков для таких типов, каки, по-прежнему очень неоднородна. Вследствие этих проблем представляется нецелесообразным применять abs(rational Те же самые аргументы подразумевают, что там, где в другом месте требуется абсолютное значение IntType, вычисление выполняется inline. Дэвид Абрахамс дал полезные отзывы о документации. Долгое обсуждение достоинств обеспечения перехода от плавающей точки к рациональной состоялось в ноябре 2000 года. Ключевые участники включали Реджи Сигрейвса, Лутца Кеттнера и Дэниела Фрая (хотя большая часть списка, казалось, была вовлечена в тот или иной момент!). Несмотря на то, что конечным результатом было решениененичего реализовать, обсуждение было очень ценным для понимания вопросов. Стивен Сильвер внес полезный вклад в использование рационального класса с определяемым пользователем целым типом. Николай Младенов обеспечил текущую реализацию оператора+= и оператор-=. Обсуждение вопросов, связанных с Koenig lookup и std::swap, состоялось в январе 2001 года. Дэрил Уокер (Daryle Walker) в декабре 2005 года предоставил булеву конверсионную систему, позволяющую рационально использовать ее в том же булевом контексте, что и встроенные цифровые типы. Он добавил шаблон перекрестного обоснования в августе 2013 года. Пересмотрено 30 августа 2013 © Copyright Paul Moore 1999-2001; © Daryle Walker 2005, 2013. Разрешение на копирование, использование, изменение, продажу и распространение этого документа предоставляется при условии, что это уведомление об авторских правах появляется во всех копиях. Этот документ предоставляется "как есть" без явной или подразумеваемой гарантии и без претензий относительно его пригодности для любых целей. Статья Rational Number Library раздела может быть полезна для разработчиков на c++ и boost. Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта. :: Главная :: :: рекламаПреобразование из плавающей точки
Библиотека не предлагает функцию преобразования из плавающей точки в рациональную. Был получен ряд запросов на такое преобразование, но обширные дискуссии по списку повышения пришли к выводу, что «лучшего решения» проблемы нет. Поскольку нет причин, по которым пользователь библиотеки не может написать свою собственную функцию преобразования, которая соответствует его конкретным требованиям, было принято решение не выбирать какой-либо один алгоритм в качестве «стандарта».
// These two values could in practice be obtained from user input,
// or from some form of measuring instrument.
double x = 1.0;
double y = 3.0;
double z = x/y;
rational<I> r = rational_from_double(z);
>Абсолютное значение
В первом случае представляется логичным реализовать abs(rational
template <typename IntType>
inline rational<IntType> abs(const rational<IntType>& r)
{
if (r.numerator() >= IntType(0))
return r;
return rational<IntType>(-r.numerator(), r.denominator());
}
>Ссылки
История и признания
В декабре 1999 года я внедрил начальную версию класса рациональных чисел и представил ее в список рассылкиboost.org. Некоторое обсуждение вопроса об осуществлении состоялось в перечне рассылки. В частности, Эндрю Д. Джуэлл указал на важность обеспечения минимизации риска перелива и обеспечения беспереливных реализаций большинства основных операций. Название rational_cast предложил Кевлин Хенни. Эдди Брей предоставил неоценимые комментарии — не в последнюю очередь указав на некоторые довольно глупые ошибки при наборе текста в исходном коде!