Очень длинное определение PI

Я отлаживаю какой-то старый код C, и у него есть определение #define PI 3.14... где... около 50 других цифр.

Почему это? Я сказал, что могу уменьшить число примерно до 16 знаков после запятой, но мой босс огрызнулся на меня, сказав, что другие числа нужны для независимости от платформы и совместимости с предыдущими версиями. Но не замедлит ли это программу?


c pi
person P45 Imminent    schedule 13.03.2014    source источник
comment
Как это может замедлить работу программы? Вы понимаете, что это значит, что C является компилируемым языком?   -  person Ernest Friedman-Hill    schedule 14.03.2014
comment
Я не знаю. Извините, уже очень поздно, и он тоже что-то подобное сказал ;-)   -  person P45 Imminent    schedule 14.03.2014
comment
Почему вы спрашиваете, не замедлит ли это программу, когда вас просят отлаживать, а не оптимизировать?   -  person nhgrif    schedule 14.03.2014
comment
Наверное, нет, и кого это волнует? Если у вас нет доказательств того, что ваша программа слишком медленная, вам не следует заморачиваться микрооптимизациями. Если ваша программа слишком медленная, возьмите профилировщик — найдите самую медленную часть, а затем ускорьте ее.   -  person Pete Baughman    schedule 14.03.2014
comment
Он жалуется, что код медленный. Я думаю, он имеет в виду отладку, чтобы сделать это быстрее.   -  person P45 Imminent    schedule 14.03.2014
comment
Я пытаюсь помочь и подумал, что более короткий PI может помочь   -  person P45 Imminent    schedule 14.03.2014
comment
Вы можете прочитать ericlippert.com/2012/12/17/performance-rant если вы думаете о производительности. Это довольно проницательно.   -  person Pete Baughman    schedule 14.03.2014
comment
Извините, что расстроил. Но я до сих пор не понимаю, почему у моего босса PI с точностью до 50 знаков после запятой! Должен ли я удалить этот вопрос?   -  person P45 Imminent    schedule 14.03.2014
comment
Что касается числовых констант, то стандарт предоставляет набор из них с точностью, поддерживаемой машиной, в заголовке math.h. Вырвите (вероятно, неправильные) значения и используйте предоставленные компилятором.   -  person vonbrand    schedule 14.03.2014
comment
У вашего босса число Пи с точностью до 50 знаков после запятой, потому что он считает, что это важно. Оказывается, наверное, нет. Компилятор, вероятно, преобразует 50-значное десятичное значение во что-то с меньшей точностью, в зависимости от целевой архитектуры, но это, вероятно, тоже не имеет значения.   -  person Pete Baughman    schedule 14.03.2014
comment
@Yogi: Это не замедляет работу программы, поэтому, если это ваша главная забота, все в порядке :) Не нужно удалять вопрос, может быть, кто-то найдет время, чтобы написать явный ответ   -  person Niklas B.    schedule 14.03.2014
comment
@PeteBaughman: значит, это вообще не повлияет на производительность? даже длинный дубль на солярисе? Поставьте как ответ, и я проголосую и приму ;-)   -  person P45 Imminent    schedule 14.03.2014
comment
Что означает -1 в вопросе? Могу ли я улучшить его?   -  person P45 Imminent    schedule 14.03.2014
comment
@Yogi: Буквальное само по себе ни на что не повлияет. Однако использование long double вместо double или float может повлиять на производительность.   -  person Niklas B.    schedule 14.03.2014


Ответы (2)


Нет, это не замедлит работу программы, если только вы не используете невероятно маломощный чип DSP с частотой 1 МГц, который должен выполнять арифметические операции с плавающей запятой в программном обеспечении, а не передавать их выделенному FPU. Это означало бы, что любые математические операции, использующие данные с плавающей запятой, выполняются намного медленнее, чем просто использование целочисленной арифметики.

В общем, большая точность приведет к замедлению только в том случае, если наиболее трудоемкая часть вашей программы выполняет множество вычислений в быстрой последовательности, а вычисления с плавающей запятой особенно медленны. В современных процессорах это, как правило, не так, за возможным исключением некоторых микросхем, которые вызывают 80-тактную остановку в таких вещах, как потеря значимости с плавающей запятой. Такая проблема, вероятно, выходит за рамки этого вопроса.

Во-первых, лучше использовать общее стандартное определение PI, как в стандартном заголовке C, <math.h>, где он определяется как #define M_PI 3.14159265358979323846. Если вы настаиваете, вы можете определить его вручную.

Кроме того, лучшая точность, доступная в настоящее время в C, эквивалентна примерно 19 цифрам.

Согласно Википедии, 80-битный «Intel» IEEE 754 с расширенной точностью long double, который состоит из 80 бит, дополненных до 16 байтов в памяти, имеет 64-битную мантиссу без неявного бита, что дает вам 19,26 десятичных цифр. Это был почти универсальный стандарт для длинных двойников на протяжении веков, но в последнее время все начало меняться.

Более новый 128-битный формат с четырехкратной точностью имеет 112 бит мантиссы плюс неявный бит, что дает вам 34 десятичных разряда. GCC реализует это как тип __float128, и есть (если память не изменяет) опция компилятора, чтобы установить для него long double.

Лично, если бы мне пришлось использовать наше собственное определение числа пи, я бы написал что-то вроде этого:

#ifndef M_PI
#define PI 3.14159265358979323846264338327950288419716939937510
#else
#define PI M_PI
#endif

Если последний стандарт C поддерживает еще более широкий тип примитивных данных с плавающей запятой, это в значительной степени гарантирует, что константы в математической библиотеке будут обновлены для поддержки этого.

Ссылки

  1. Более точные типы данных с плавающей запятой, чем double?, дата обращения 13 марта 2014 г., <https://stackoverflow.com/questions/15659668/more-precise-floating-point-data-types-than-double>
  2. Математическое постоянное значение PI в C, дата обращения 13 марта 2014 г., <https://stackoverflow.com/questions/9912151/math-constant-pi-value-in-c>
person Cloud    schedule 13.03.2014
comment
M_PI не определен стандартом C, поэтому он может быть недоступен. (Это требуется POSIX.) И я думаю, что вы хотите #ifndef M_PI, а не #ifndef PI. - person Keith Thompson; 14.03.2014
comment
Спасибо большое. И я вижу точно такой же код, как и у вас. - person P45 Imminent; 14.03.2014
comment
Вы серьезно относитесь к формальному цитированию вопросов о переполнении стека? Если да, то почему бы и туда не поставить хотя бы гиперссылку? - person Niklas B.; 14.03.2014
comment
@НикласБ. Я сделал. Ознакомьтесь с разделом References внизу моего ответа. Я использую сценарий GreaseMonkey, который написал, чтобы отслеживать сайты, которые я начинаю посещать при написании ответа. Я цитирую не только ТАК ответы. Кроме того, блок-ответ из одного из моих источников цитируется, поэтому я почти уверен, что придерживаюсь требований цитирования IEEE. Мне не нравятся автоматические гиперссылки, потому что я хочу, чтобы фактический URL-адрес был включен в ответ на случай, если он когда-либо будет повторно опубликован на другом сайте. - person Cloud; 14.03.2014
comment
@KeithThompson Вот что я хотел сделать. Спасибо, что поймали это! - person Cloud; 14.03.2014
comment
@NiklasB: О, да, на самом деле я имел в виду раздел ссылок. Я хотел бы видеть там интерактивные ссылки ;) Вы можете легко сделать сам URL ссылкой. Может быть, простое дополнение к вашему сценарию? Довольно странные вещи. - person Niklas B.; 14.03.2014
comment
@Dogbert: URL-адреса в вашем разделе ссылок не распознаются как гиперссылки, вероятно, из-за окружающих символов < и >. В любом случае, я не думаю, что такая формальность необходима при размещении ссылок на одном и том же сайте. - person Keith Thompson; 14.03.2014
comment
@НикласБ. Спасибо! Я мог бы включить их, но я хочу, чтобы они были одинаковыми для всех записей в моем разделе «Ссылки». Позволяет ли SO форматировать страницы, отличные от SO, в виде гиперссылок HTML, по которым можно щелкнуть? Кажется, я не могу заставить их работать. - person Cloud; 14.03.2014
comment
@Dogbert: я исправил это для тебя. Я также думаю, что вам не хватает ссылки из цитаты на ссылку (или как там это называется) - person Niklas B.; 14.03.2014
comment
@KeithThompson Включение шевронов <> является преднамеренным, так как они обычно используются во многих технических документах и ​​статьях, когда я их пишу. Я также хочу, чтобы имя страницы и URL отображались одновременно. - person Cloud; 14.03.2014
comment
И есть мысли, что ТАК было скорее развлечением, чем работой. Я думаю, вы только что доказали, что я ошибаюсь, но я не уверен - person Niklas B.; 14.03.2014
comment
@НикласБ. Спасибо. Шрифт моноширинный и кликабельный. Ура! Да, ТАК весело, просто у меня много привычек, от которых трудно избавиться спустя столько лет. Кроме того, мне нравится следить за тем, чтобы источники получали как можно больше информации, поскольку они заслуживают нескольких голосов, если их сочтут достоверными. - person Cloud; 14.03.2014

Количество цифр в определении макроса почти наверняка не повлияет на производительность во время выполнения.

Расширение макроса текстовое. Это означает, что если у вас есть:

#define PI 3.14159... /* 50 digits */

тогда каждый раз, когда вы ссылаетесь на PI в коде, для которого видно это определение, это будет так, как если бы вы выписали 3.14159....

C имеет только три типа с плавающей запятой: float, double и long double. Там размеры и точность определяются реализацией, но обычно они составляют 32 бита, 64 бита и что-то шире 64 бит (размер long double обычно больше варьируется от системы к системе, чем два других).

Если вы используете PI в выражении, оно будет оцениваться как значение определенного типа. И на самом деле, если литерал не имеет суффикса L, он будет типа double.

Итак, если вы пишете:

double x = PI / 2.0;

как будто вы написали:

double x = 3.14159... / 2.0;

Компилятор, вероятно, оценит деление во время компиляции, генерируя значение типа double. Любая дополнительная точность литерала будет отброшена.

Чтобы убедиться в этом, вы можете попробовать написать небольшую программу, использующую макрос PI, и изучить ассемблерный листинг.

Например:

#include <stdio.h>

#define PI 3.141592653589793238462643383279502884198716939937510582097164

int main(void) {
    double x = PI;
    printf("x = %g\n", x);
}

В моей системе x86_64 сгенерированный машинный код не имеет ссылки на значение полной точности. Инструкция, соответствующая инициализации:

movabsq $4614256656552045848, %rax

где 4614256656552045848 — это 64-битное целое число, соответствующее двоичному представлению числа двойной точности IEEE, максимально близкому к 3.141592653589793238462643383279502884198716939937510582097164.

Фактическое сохраненное значение с плавающей запятой в моей системе оказывается точно таким:

3.1415926535897931159979634685441851615905761718750000000000000000

из которых только около 16 десятичных цифр являются значимыми.

person Keith Thompson    schedule 13.03.2014
comment
Boss явно готовится к 256-битным числам с плавающей запятой: независимость от платформы и совместимость с будущими версиями - person Niklas B.; 14.03.2014
comment
@NiklasB.: Вполне возможно - и это вполне разумно, поскольку нет затрат на время выполнения. - person Keith Thompson; 14.03.2014