Есть довольно глупая проблема с числом пи в C и C++. Насколько мне известно, M_PI, определенный в math.h, не требуется ни одним стандартом.
Новые стандарты C++ ввели много сложной математики в стандартную библиотеку - гиперболические функции, std::hermite и std::cyl_bessel_i, различные генераторы случайных чисел и так далее.
Введены ли какие-либо из «новых» стандартов в константу для числа Пи? Если нет - почему? Как без этого будет работать вся эта сложная математика?
Мне известны аналогичные вопросы о пи в C++ (им несколько лет и стандарты); Хотелось бы узнать текущее состояние проблемы.
Меня также очень интересует почему ой почему C++ по-прежнему не имеет константы пи, но имеет много более сложной математики.
Я знаю, что могу определить пи как 4*atan(1), acos(1) или double pi = 3.14;. Конечно. Но почему я все еще должен это делать? Как стандартные математические функции работают без пи?
Я думаю, что, возможно, стоит добавить новые ответы, поскольку C++ 20 добавил константу pi, насколько мне известно
@GuillaumeRacicot, я обновил вопрос. Не уверен, стоит ли нам обращаться к C++ 20, поскольку он еще не выпущен официально.
@GuillaumeRacicot: Уже немного поздно добавлять…





Вплоть до C++ 20 нет, ни один из стандартов не вводил константу, которая представляла бы число пи (π). Вы можете приблизить число в своем коде:
constexpr double pi = 3.14159265358979323846;
Другие языки, такие как C# имеют - константа, объявленная в их библиотеках.
Обновлять:
Начиная с C++ 20, внутри заголовка pi действительно объявлена константа <numbers>. Доступ к нему осуществляется через: std::numbers::pi.
Возможно, вы захотите добавить inline для C++ 17 +.
Та. Проголосуйте за, но обратите внимание, что ваше определение все еще уязвимо для неточностей с платформами с разными определениями double. В C# это просто, так как тип double исправлен. Если бы я был в комитете по стандартам C++, я бы предложил что-то вроде std::constants<double>::pi.
@Deduplicator также не является неявно встроенным constexpr ...?
@Bathsheba: double - это IEEE double в любой разумной реализации (и везде, где C соответствует Приложению F; я не уверен, есть ли у C++ аналог, но он должен). Нет веских причин рассматривать double как тип, точность которого может варьироваться. Если хотите, используйте long double.
@Р. Достаточно справедливо, хотя в этом случае вам следует статическое утверждение на std::numeric_limits<double>::is_iec559;. Что, признаюсь, и есть у меня в "главном заголовке". Обратите внимание, что формально вам нужно проверять все типы с плавающей запятой отдельно. То, что один из них - IEEE754, не означает, что они все.
@snb: Для функций, и поскольку C++ 17, который это разрешил и ввел встроенные переменные, конечно же, constexpr подразумевает inline. Но объявления на уровне пространства имен внезапно не изменили своего значения.
@snb constexprне подразумеваетinline для переменной области пространства имен [dcl.constexpr]: Функция или статический член данных, объявленная с помощью спецификатора constexpr, неявно является встроенной функцией или переменной
А что насчет чего-то вроде 0x3.243F6A8885A308D3p+0, который дал бы абсолютно понять, сколько бит точности вы даете?
@DanielSchepler Итак, что это за «число»? Я не знал, что есть двойные числа с основанием 16.
Вплоть до C++ 17 включительно Пи не является константой, введенной в язык, и это головная боль.
Мне повезло, что я использую увеличение, и они определяют Пи с достаточно большим количеством десятичных знаков даже для 128-битного long double.
Если вы не используете Boost, запрограммируйте его самостоятельно. Заманчиво определить его с помощью тригонометрической функции, но если вы это сделаете, вы не сможете сделать его constexpr. Точность тригонометрических функций также не гарантируется никаким известным мне стандартом (ср. std::sqrt), так что вы действительно находитесь на опасной почве, полагаясь на такую функцию.
Есть способ получить значение constexpr для Пи с помощью метапрограммирования: см. http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/
От C++ 20 хорошие новости. является - определение для Пи. C++ 20 добавляет некоторые математические константы в <numbers>. Например, std::numbers::pi относится к типу double.
Ссылка: https://en.cppreference.com/w/cpp/numeric/constants
Как кто-то предположил, есть еще const long double PI = acosl(-1.0L);.
@Lundin: Но, к сожалению, это не может быть constexpr, поэтому я говорю: «Определение его с помощью функции триггера - это боль»
Почему это имеет значение? Pi - это константа, не подлежащая изменению или чему-либо, зависящему от реализации. Просто запишите значение (в объекте const, если хотите) количества знаков, значимых для double (или какое-нибудь нелепое число, если вас интересуют гипотетические действительно длинные удвоения).
@ R .. Моя проблема в том, что то, что сейчас кажется смешным, может стать совершенно нормальным через 20 лет или около того. (На ум приходят 640 тысяч Билла Гейтса). Я верю, что Boost будет идти в ногу с эволюцией архитектуры.
Запоминать значение числа Пи намного проще, чем запоминать формулу. (хотя я вообще не пытался запоминать формулу ...)
@Bathsheba Но действительно ли это актуально? Насколько я понял, ~ 40 цифр числа Пи - это достаточная точность, и всегда будет достаточно точности для любых вычислений, позволяющих достичь субатомной точности в масштабе всей Вселенной.
@KonradRudolph - Я думал, что это было в 80-х: на основе логарифма числа кубов длиной 1 планка, составляющих наблюдаемую Вселенную. Интересно, есть ли у кого-нибудь номер поближе? И я не мог не заметить, что вы отредактировали свой комментарий; удвоение числа! Если вы удвоите его снова, вы окажетесь там, где я!
@Bathsheba Я только смутно вспомнил и обновил номер, когда нашел ссылку.
@KonradRudolph - это действительно жалкий ответ: много физики происходит на субпротонном уровне. На данный момент мой аргумент о кубе длины Планка верен, и качество ответов SO, очевидно, лучше, чем на сайте fizzix.
@Bathsheba Я не думаю, что большая часть физики (или любой, на самом деле) требует одновременной точности в субпротонном и макроскопическом (Вселенском) масштабе. Другими словами, динамический диапазон в этом (по общему признанию, не очень хорошо написанном) ответе уже преувеличен для всех практических целей. Тем не менее, я не исключаю обратного.
@KonradRudolph: Вы абсолютно правы, но C++ гордится тем, что является языком общего назначения, поэтому, если вам нужна необычная точность с плавающей запятой, он должен ее обеспечить. Многие физики увязли в том факте, что G Ньютона имеет ужасающую точность.
Насколько нужен был бы пи, если бы стандартная библиотека включала набор триггерных функций, измеряющих углы в единицах радианов 2pi? Хотя есть несколько целей, связанных с производными, интегралами и дифференциальными уравнениями, для которых полезны радианы, для большинства целей, связанных с фактическими углами, они являются примерно худшей единицей, которую можно вообразить.
@supercat: он всплывает повсюду. Вероятность, финансовая математика, решения с использованием библиотек комплексных чисел, являющиеся некоторыми приложениями. C++ - это язык общего назначения, и поэтому IMHO должен иметь в своем стандарте pi.
@Bathsheba: Да, я полагаю, радианы в конечном итоге становятся естественной единицей для некоторых видов операций с комплексными числами из-за равенства Эйлера, но меня все еще раздражает, что нет никаких триггерных функций, которые предназначены для использования с фактическим углы. Например, использование графического преобразования "поворота" в радианах кажется мне просто тупым, поскольку, скорее всего, угол будет несколько кратным пи / 4, но четырехкратный поворот на пи / 4 не совсем дает исходная ориентация.
@supercat В любом случае вы должны использовать кватернионы для вращения.
@KonradRudolph: высокоточный Пи имеет значение при реализации уменьшения дальности. Например, внутренняя константа Pi x86 / x87 (полная 64-битная мантисса) приводит к наихудшей ошибке для небольших входов в инструкцию fsin"около 1,37 квинтиллиона единиц в последнем месте, оставляя менее четырех бит правильным", и еще хуже для больших входов, где уменьшение диапазона повторяется несколько раз. Это несколько косвенно по отношению к константам, используемым для long double в C++, но в любом случае аккуратно.
@KonradRudolph: Численное вычисление функций и констант, требующих 5000 десятичных цифр, использовалось с алгоритмом PSQL, чтобы вывести понимание теоретической физики и сократить вычисление многомерных интегралов до констант времени компиляции. См. Работу Дэвида Бейли по этому поводу. Boost.math вычисляет число Пи динамически, если оно не находится в списке предварительно вычисленных типов; это было бы требованием для любого стандартного значения пи C++.
В качестве простого эксперимента постройте sin(pi*x) с 16-значным числом пи для double. Наблюдайте за дрейфом нулей греха на x * epsilon(). Было бы хорошо иметь sin_pi() и т. д., Но иногда вам просто нужно пи, потому что ваша проблема не отображается как x * pi. Вам может потребоваться более высокая точность, чем рабочая точность pi, чтобы предотвратить это наложение. Это может быть реализовано с помощью шаблонов выражений constexpr для основных типов. Объект pi для фундаментальных типов может быть неоцененной суммой типа float - полиномом в epsilon (), если хотите.
В стороне, я думаю, что все основные математические функции могут иметь версии constexpr noexcept, если они имеют код ошибки (нет записи в глобальный и неизменно игнорируемый errno). double sin(double x, error_code& ec); и auto [lgval, lgsign] = lgamma(x, ec); и др.
Если вам абсолютно необходима точность до определенного количества цифр, почему вы используете числа с плавающей запятой, а не фиксированные?
@Cubic: с плавающей запятой работают очень быстро, и, если вы знаете, как они себя ведут (накопление ошибок и т. д.), Они являются идеальным инструментом для многих приложений физического и финансового моделирования.
Точность π в Boost на самом деле находится в забавной ситуации: с одной стороны, у нас есть указанная вами константа, а с другой - это, что связано с 7-летняя ошибка.
Никому не повезло использовать Boost
Как говорили другие, std::pi нет, но если вам нужно точное значение PI, вы можете использовать:
constexpr double pi = std::acos(-1);
Это предполагает, что ваша реализация C++ создает правильно округленное значение PI из acos(-1.0), что является обычным, но не гарантируется.
Это не constexpr, но на практике оптимизирующие компиляторы, такие как gcc и clang, оценивают его во время компиляции. Однако объявление const важно для оптимизатора, чтобы он хорошо справлялся.
Это опасно, потому что функция acos() имеет бесконечный наклон на x = -1. Следовательно, этот метод полагается на реализацию acos(), чтобы в основном явно улавливать случай точного аргумента -1 и напрямую возвращать правильную константу. Лучше использовать что-то вроде 4*atan(1), который математически намного более устойчив (хороший наклон на x = 1 и умножение на 4 всегда точное с математикой с плавающей запятой).
Нам не разрешается использовать std::acos в постоянном выражении. clang сообщает об этом как об ошибке. Обратите внимание, что это несоответствующее расширение, и в конечном итоге его следует исправить в gcc. Пожалуйста, обратитесь к этому отвечать для получения более подробной информации.
Это не очевидно хорошая идея, потому что не существует очевидного типа, с помощью которого определяется pi, универсально применимого во всех доменах.
Пи, конечно, иррациональное число, поэтому оно не может быть правильно представлено типом любой C++. Вы можете возразить, что естественный подход состоит в том, чтобы определить его в самом большом доступном типе с плавающей запятой. Однако размер самого большого стандартного типа с плавающей запятой long double не определен стандартом C++, поэтому значение константы будет варьироваться в зависимости от системы. Хуже того, для любой программы, в которой рабочий тип не был этим самым большим типом, определение числа "пи" было бы неуместным, поскольку оно потребовало бы снижения производительности при каждом использовании числа "пи".
Для любого программиста также тривиально найти значение пи и определить свою собственную константу, подходящую для использования, поэтому включение ее в математические заголовки не дает большого преимущества.
C++ 14 дал нам шаблоны переменных. Разве они не для этого?
говорите как настоящий член комитета, говорите обо всех способах, которые невозможно сделать, несмотря на то, что был представлен ясный путь вперед. См. Удивительно подходящий пример шаблонов переменных.. Не думаю, что ваш ответ плох, но уверен, что он не поможет вечной депрессии Бьярна Страуструпа из-за сожаления о передаче контроля над будущим C++ очень нерешительному комитету.
@snb Я согласен с тем, что превращение pi в полиморфную константу - это ясный путь вперед - на языке с выводом типа Хиндли-Милнера. В Haskell у нас всегда был pi :: Floating a => a, так что pi автоматически будет иметь значение 3.1415927 в контексте Float, 3.141592653589793 в контексте Double и π в контексте символьных вычислений. Но действительно ли люди хотели бы явно создавать экземпляр параметра шаблона? Это кажется немного неудобным, особенно если фиксированная реализация long double будет давать идентичные результаты в большинстве приложений.
Существует несколько независимых от платформы методы, изложенные здесь ... поэтому, хотя было бы неплохо иметь стандартизированный, у нас есть варианты.
Что ж, у нас также может быть новый пустой тривиальный тип __exposition_pi_t с неявным преобразованием ко всем типам чисел.
@leftaroundabout Я считаю, что писать auto a = pi<float>; совершенно нормально, определенно более читабельно, чем пресловутый 4*atan(1)
@Amomum да ... люди, вероятно, по-прежнему будут использовать мономорфные сокращения локально, чтобы избавиться от математических формул, но я согласен, что auto pi = std::pi<double> выглядит довольно чистым.
Я могу возразить, что определение M_PI как десятичной константы с «достаточным» количеством цифр будет работать хорошо, без каких-либо проблем, о которых вы упомянули. Или, если набрать является по какой-то причине является проблемой, тогда также определите M_PI_F и M_PI_LD (последний указан для 128-битных реализаций).
Ух, я отклонил это ошибочным щелчком, и теперь я не могу его отменить. Простите. Это заслуживает +1 за довольно хорошее объяснение аргументов комитета по поводу того, почему он не был добавлен, даже если я лично считаю, что рассуждения в корне ошибочны по причинам, которые люди уже указали.
Они легко могли бы добавить функцию std::pi<T>(), которая гарантированно будет определена для T, являющегося всевозможным плавающим. Я не так хорошо знаком с новомодной идеей constexpr, но, по-видимому, функция тоже может быть отмечена как это.
M_PI определяется «стандартом», если не стандартом язык: POSIX с расширением X / Open System Interfaces (которое очень часто поддерживается и требуется для официального оформления UNIX).
Между тем, C++ 20 имеет такие константы (объединенный в последнем раунде функций C++ 20). В частности, есть как std::numbers::pi (типа double), так и шаблон переменной, который вы можете использовать, если хотите другой тип с плавающей запятой, например std::numbers::pi_v<float>.
Не стесняйтесь перефразировать мою правку, как сочтете нужным - я просто хотел добавить сюда фактическое написание новых констант для потомков.
Отредактировано - удалить термин необходимо, потому что он оказался спорным. Это слишком абсолютный термин.
C++ - большой и сложный язык, по этой причине Комитет по стандартам включает только те вещи, которые являются настоятельно требуется. Насколько это возможно, оставлено неязыковым стандартным библиотекам ... например, Boost.
boost :: math :: constants
Конечно, std::hermite, std::cyl_bessel_i, std::cosh, std::mersenne_twister_engine, std::ranlux48, std::cauchy_distribution, std::assoc_laguerre и std::beta были абсолютно необходимы, мы все используем их каждый день!
Я почти уверен, что вы не смогли заставить даже сам комитет подписать идею о том, что все, за что они голосуют, «абсолютно необходимо». Речь идет не о необходимости, а о добавлении ценности языку - почти ничего такого в стандартной библиотеке необходим для написания программ, и в этом отношении большая часть основного языка также может быть отброшена (если вы не против работать в Брезент Тьюринга). Значение включения константы для числа Пи можно обсуждать, но идея о том, что ее здесь нет, потому что она просто не нужна, не выдерживает критики.
Не жалуйтесь на меня, я просто цитирую комитет по стандартам. Долгие открытые дискуссии о том, сколько повышения должно быть включено в стандарт C++ 11. Причина, по которой его сделали такое небольшое подмножество, заключается в том, что авторы компилятора жаловались на то, сколько тестирования было задействовано. Следовательно, если что-то есть в стандартах, это потому, что кто-то посчитал необходимым стандартизировать это. То, что вы не знаете почему, не означает, что причины не было.
@ Tiger4Hire Я уверен, что для всего есть причина, я просто не могу понять, почему константа для пи не была добавлена, когда было много более сложных вещей. Константы легко написать с помощью шаблонов переменных и не потребуют длительного тестирования со стороны разработчиков компилятора.
@Amomum: Да, добавление числа Пи кажется небольшими накладными расходами для большого выигрыша. Имейте в виду, лично я бы предпочел видеть std :: network перед константами std :: math ::.
Вы заметили наличие старых вопросов, таких как Лучшая независимая от платформы константа Пи?. Если вы беспокоитесь, что они устарели, вы всегда можете назначить вознаграждение за одного из них, запрашивающего ответы на основе C++ 17 и т.д ... Тогда все ответы будут в одном месте. Почему все еще хороший вопрос, но, возможно, он должен быть сосредоточен на том, почему, и просьба обновлять данные должна быть наградой по существующим вопросам.