LINUX.ORG.RU

Число Пи в Си

 


0

4

Здравствуйте!

Константа Pi определена в файле math.h следующим образом:

cat /usr/include/math.h | grep M_PI
# define M_PI		3.14159265358979323846	/* pi */

Теперь я хочу вывести ее на экран:

printf("M_PI = %.25f\n",M_PI);

И получаю результат:

M_PI = 3.1415926535897931159979635

Уважаемые знатоки, внимание вопрос: почему значения не совпадают?

Deleted

Константа Pi определена в файле math.h следующим образом

Это не константа, это макрос, просто строка текста

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Это не константа, это макрос, просто строка текста

Строка текста — это M_PI, а результат макроса именно константное число.

vodz ★★★★★
()

Уважаемые знатоки, внимание вопрос: почему значения не совпадают?

Ну как сказать:


$ cat /usr/include/math.h | grep M_PI
# define M_PI		3.14159265358979323846	/* pi */
# define M_PI_2		1.57079632679489661923	/* pi/2 */
# define M_PI_4		0.78539816339744830962	/* pi/4 */
# define M_PIl		3.141592653589793238462643383279502884L /* pi */
# define M_PI_2l	1.570796326794896619231321691639751442L /* pi/2 */
# define M_PI_4l	0.785398163397448309615660845819875721L /* pi/4 */
# define M_PIf16	__f16 (3.141592653589793238462643383279502884) /* pi */
# define M_PI_2f16	__f16 (1.570796326794896619231321691639751442) /* pi/2 */
# define M_PI_4f16	__f16 (0.785398163397448309615660845819875721) /* pi/4 */
# define M_PIf32	__f32 (3.141592653589793238462643383279502884) /* pi */
# define M_PI_2f32	__f32 (1.570796326794896619231321691639751442) /* pi/2 */
# define M_PI_4f32	__f32 (0.785398163397448309615660845819875721) /* pi/4 */
# define M_PIf64	__f64 (3.141592653589793238462643383279502884) /* pi */
# define M_PI_2f64	__f64 (1.570796326794896619231321691639751442) /* pi/2 */
# define M_PI_4f64	__f64 (0.785398163397448309615660845819875721) /* pi/4 */
# define M_PIf128	__f128 (3.141592653589793238462643383279502884) /* pi */
# define M_PI_2f128	__f128 (1.570796326794896619231321691639751442) /* pi/2 */
# define M_PI_4f128	__f128 (0.785398163397448309615660845819875721) /* pi/4 */
# define M_PIf32x	__f32x (3.141592653589793238462643383279502884) /* pi */
# define M_PI_2f32x	__f32x (1.570796326794896619231321691639751442) /* pi/2 */
# define M_PI_4f32x	__f32x (0.785398163397448309615660845819875721) /* pi/4 */
# define M_PIf64x	__f64x (3.141592653589793238462643383279502884) /* pi */
# define M_PI_2f64x	__f64x (1.570796326794896619231321691639751442) /* pi/2 */
# define M_PI_4f64x	__f64x (0.785398163397448309615660845819875721) /* pi/4 */

anonymous
()
Ответ на: комментарий от anonymous

там же 16 значащих чисел ему и вывело, что двойную точность обеспечило, а дальше ему не обязаны точно выводить.

grem ★★★★★
()

ты ещё в фортране литералы не выводил на экран или не присваивать их чему-нить, там при их объявлении свои заморочки

grem ★★★★★
()

Кстати, M_PI не включено в какой-либо стандарт C или С++. Так что его даже и не должно быть в стандартном math.h (или cmath). Однако POSIX таки определаяет M_PI

KennyMinigun ★★★★★
()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от utf8nowhere

Бывают неконстантные числа?

Ну в двоичной арифметике — запросто. 0.5=0.4(9)8 и количество девяток зависит от размерности под действительное число и можно выразить дробью с квалификаторами/приведением типа. С pi ещё сложнее, оно — иррациональное, но ведь «число».

vodz ★★★★★
()
Ответ на: комментарий от utf8nowhere

Так числа с разным количеством девяток это просто разные числа.

Но в результате сравнения этих констант 0.5 и 0.4(9)8 вы получите равенство.

Где тут неконстантность одного числа?

Оно неконстантное для разных архитектур. И 1/2 — это вроде константное выражение, но — нет. И если проблемы реальных чисел более-менее известны, то со значением pi в math.h вообще нельзя быть уверенным, что оно вот такое определенное будет везде при компиляции из исходников.

vodz ★★★★★
()

А почему они должны совпадать? Понятие совпадения к числам с плавающей точкой вообще не применимо.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Просто странно.

Насколько я понял, речь идет о «числах двойной точности», т.е. мне «гарантируется» точность до 15-16 знаков после запятой.

Но для чего тогда было объявлять константу с большим количеством знаков?

Почему printf вывело мне мусор, вместо того чтобы заполнить нулями после 15-16-и знаков?

Я могу быть уверен при проведении численных расчетов, что у меня не возникнет какой-то мусор? К примеру, если я буду использовать константу M_PI?

Вдруг я захочу расчитать траекторию полета ракеты на Марс, и из-за какого-то «мусора» получу ошибку, и ракета улетит на Солнце? (на самом деле я не захочу)

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

Почему printf вывело мне мусор, вместо того чтобы заполнить нулями после 15-16-и знаков?

Потому что машина сделала то что ты сказал, а не то что ты хотел.

Я могу быть уверен при проведении численных расчетов, что у меня не возникнет какой-то мусор?

Спец который не понимает ограничений машины не может в численные расчёты с применением машины.

ados ★★★★★
()
Ответ на: комментарий от Deleted

Ну прочти ты наконец статью с самого первого каммента.

ados ★★★★★
()
Ответ на: комментарий от Deleted

Насколько я понял, речь идет о «числах двойной точности», т.е. мне «гарантируется» точность до 15-16 знаков после запятой.

Её ты тут и видишь.

Но для чего тогда было объявлять константу с большим количеством знаков?

Это не константа, а макрос, раскрывающийся в floating point литерал. Если что, его можно использовать не только чтобы положить в double. Можно положить в long double. Можно использовать в константном выражении, требующем бОльшую точность.

Почему printf вывело мне мусор, вместо того чтобы заполнить нулями после 15-16-и знаков?

С чего ты взял что это мусор? Это точное представление того числа что ты передал в printf ограниченное указанным тобой числом знаков. Значение double в десятичном виде это бесконечная периодическая дробь, если добавишь знаков, то увидишь повторяющуюся часть.

Вдруг я захочу расчитать траекторию полета ракеты на Марс, и из-за какого-то «мусора» получу ошибку, и ракета улетит на Солнце? (на самом деле я не захочу)

Для таких целей существуют более точная и контролируемая арифметика, типа gmp. А используя встроенную нужно иметь чёткое представление о её особенностях и ограничениях.

slovazap ★★★★★
()
Ответ на: комментарий от Deleted

Потому что это число точно представимо как в виде двоичной, так и в виде десятичной дроби.

slovazap ★★★★★
()
Ответ на: комментарий от t184256

Вот такие умные слова знает, а тип «результата макроса» - нет.

Долго думал как обосраться? Нет никакого «типа результата макроса». Макрос — текстовая подстановка, например:

#define COMMA ,
Какой у этого макроса «тип результата»?

vodz ★★★★★
()
Ответ на: комментарий от PPP328

Потому же, почему 0.3 + 0.4 != 0.7

Не совсем по тому же :) В топикстарте «другое число» банально из-за более низкой одинарной точности. А в этом примере результат не совпадает из-за невозможности представить в общем случае десятичную дробь точной двоичной.

KRoN73 ★★★★★
()
Ответ на: комментарий от Deleted

я получаю нули а не мусор

0.5 = 1/2 — точно представляется двоичной дробью.

А вот так что получится?

double a = 0.6;
printf("a = %.20f", a);

KRoN73 ★★★★★
()
Ответ на: комментарий от PexuOne

%.25f

чтение памяти за переменной?

Хоть 100f — считаются только sizeof(float) байт. В частности, на Intel — 4 байта.

KRoN73 ★★★★★
()
Ответ на: комментарий от anonymous

Число пи приблизительно равно 3

anonymous
()
Ответ на: комментарий от utf8nowhere

ты удивишься, узнав сколько раз менялось значение универсальной газовой постоянной. и оно не раз ещё поменяется

grem ★★★★★
()
Ответ на: комментарий от Deleted

Я могу быть уверен при проведении численных расчетов, что у меня не возникнет какой-то мусор?

Ты можешь быть уверен, что у тебя возникнет какой-то мусор. А сколько и какой зависит от того как ты будешь считать.

no-such-file ★★★★★
()
Ответ на: комментарий от Deleted

Вдруг я захочу расчитать траекторию полета ракеты на Марс, и из-за какого-то «мусора» получу ошибку, и ракета улетит на Солнце? (на самом деле я не захочу)

Виноваты будут руководители, который допустили макаку к расчётам, не убедившись в её профессиональной компетенции.

aedeph_ ★★
()
Ответ на: комментарий от KRoN73

А если переменная double (8 байт), то считаются тоже только 4 байта? Кстати, там ведь не указано, какой тип имеет константа M_PI.

Deleted
()
Ответ на: комментарий от Deleted

А если переменная double (8 байт), то считаются тоже только 4 байта?

Считается столько, сколько указано в спецификаторе типа printf. Поэтому, если спецификатор не соответствует реальной переменной, то будет или мусор, или ругань компилятора.

┌─( ✔ 12:37:29 +00:00:28.066):~/Sync/Works/Programming/C++
└balancer@home-server─> cat printf-error.cpp
#include <stdio.h>

int main()
{
        int x = 1;
        printf("x = %ld", x);
}
┌─( ✔ 12:37:33 +00:00:03.537):~/Sync/Works/Programming/C++
└balancer@home-server─> gcc printf-error.cpp
printf-error.cpp: In function ‘int main()’:
printf-error.cpp:6:21: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘int’ [-Wformat=]
  printf("x = %ld", x);
                     ^


┌─( ✔ 12:37:59 +00:00:24.460):~/Sync/Works/Programming/C++
└balancer@home-server─> cat printf-error.cpp
#include <stdio.h>

int main()
{
        int x = 1;
        printf("x = %ld", (long)x);
}
┌─( ✔ 12:38:01 +00:00:01.678):~/Sync/Works/Programming/C++
└balancer@home-server─> gcc printf-error.cpp
┌─( ✔ 12:38:02 +00:00:01.205):~/Sync/Works/Programming/C++
└balancer@home-server─> ./a.out
x = 1

KRoN73 ★★★★★
()
Ответ на: комментарий от Deleted

какой тип имеет M_PI не имеет значения, printf(), если сможет, будет рассматривать его как число с плавающей точкой, в данном случае как double.


Всё что за пределами допустимой точности типа заполнится чем получится, потому, что из твоего M_PI он поместить что сможет, а дальше ему числа брать неоткуда, а нулями он заполнять не обязан. Воспринимай это как «массив», первоначально забитый мусором, в который в итоге поместили только первые 16 чисел.

grem ★★★★★
()
Ответ на: комментарий от KRoN73

Хоть 100f — считаются только sizeof(float) байт. В частности, на Intel — 4 байта.

Есть тонкости. В printf параметры передаются «по значению». В функциях с переменным числом аргументов float повышается до double, поэтому по факту вызывающая сторона делает promotion, пихает в стек, а printf из стека вычитывает уже double.

В случае со scanf передаются адреса, поэтому там %f будет считывать именно четырёхбайтный float.

i-rinat ★★★★★
()
Ответ на: комментарий от KRoN73

Вывод зависит от количества знаков после запятой:

double a = 0.6;

printf("%.5f\n",a);
printf("%.10f\n",a);
printf("%.15f\n",a);
printf("%.16f\n",a);
printf("%.18f\n",a);
printf("%.20f\n",a);

Результат:

0.60000
0.6000000000
0.600000000000000
0.6000000000000000
0.599999999999999978
0.59999999999999997780

Странно.

Deleted
()
Ответ на: комментарий от vodz

Надо же, если носом ткнуть, то ты додумался.

t184256 ★★★★★
()
Ответ на: комментарий от i-rinat

В функциях с переменным числом аргументов float повышается до double,

Переменное число аргументов тут не при чём. При любом вызове «на стек» помещается удобное для этой архитектуры выровненное слово. Фиксированные аргументы с декларацией позволяют только автоматически явно расширить слово согласно декларации. printf по идее ничего не вычисляет, потому какой более больший тип будет использован внутри перед выводом на печать значения не имеет, имеет значение только формат вывода. Потому и родились расширения __attribute__((__format__(__printf__,1,2))), которые позволяют не расширить, а только предупредить, что %ld надо явно указать конвертацию, если в аргументах передали int, а указатель передаётся явно ошибочно. А использование %f — это не дань какой-то там экономии, а просто демострация, что для результата такая точность достаточна, например. если сами вычисления были не выше точности, чем float, используя скажем исходные данные в float-ах (вот тут уже экономия).

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 2)
Ответ на: комментарий от vodz
$ cat w.c 
#include <stdio.h>
void func(void *p) {}
int main(void) {
  float a = 123;
  printf("%p\n", a);
  func(a);
}
$ gcc -Wall w.c 
w.c: In function ‘main’:
w.c:5:12: warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘double’ [-Wformat=]
   printf("%p\n", a);
           ~^     ~
           %f
w.c:6:8: error: incompatible type for argument 1 of ‘func’
   func(a);
        ^
w.c:2:17: note: expected ‘void *’ but argument is of type ‘float’
 void func(void *p) {}
           ~~~~~~^
$ 

Посмотри внимательно на типы аргументов в предупреждениях GCC. Видишь, что один и тот же аргумент в двух случаях имеет разные типы? Как думаешь, почему так?

i-rinat ★★★★★
()
Ответ на: комментарий от KRoN73

Считается столько, сколько указано в спецификаторе типа printf. Поэтому, если спецификатор не соответствует реальной переменной, то будет или мусор, или ругань компилятора.

Действительность намного интереснее.

#include <stdio.h>
#include <math.h>
#include <stdarg.h>

static void foo_f(const char *fmt, ...)
{
        va_list p;
        va_start(p, fmt);

        double d = va_arg(p, double);
        printf(fmt, d);
}

static void foo_d(const char *fmt, float f)
{
        printf(fmt, f);
}

int main(void)
{
        foo_f("%.25f\n", (float)'1');
        foo_f("%.25f\n", (float)M_PI);
        foo_f("%.25f\n", M_PI);
        foo_d("%.25f\n", (float)M_PI);
        foo_d("%f\n", M_PI);
        foo_d("%.25f\n", M_PI);
        return 0;
}
$ ./a.out
49.0000000000000000000000000
3.1415927410125732421875000
3.1415926535897931159979635
3.1415927410125732421875000
3.141593
3.1415927410125732421875000
И никакого мусора.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 2)
Ответ на: комментарий от i-rinat

Посмотри внимательно на типы аргументов в предупреждениях GCC

Это предупреждение и генерит описание printf в <stdio.h> с __attribute__, сюрприз. В отличии от ошибки передачи значения для аргумента как указателя без (крайне отвратильного) преобразования типа.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Посмотри внимательно на типы аргументов в предупреждениях GCC


Я верю, что когда-нибудь до тебя дойдёт.

i-rinat ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.