how to use PI in c99

S

Szabolcs Nagy

i've just found out (searching through n1124.pdf) that in c99 math.h
does not contain M_PI

what is the desired way to use the PI constant in a c code then?

#define PI 3.1415926535897932384626433832795
#define PI (4*atan(1))
....
?
 
J

jacob navia

Szabolcs said:
i've just found out (searching through n1124.pdf) that in c99 math.h
does not contain M_PI

what is the desired way to use the PI constant in a c code then?

#define PI 3.1415926535897932384626433832795
#define PI (4*atan(1))
...
?

The problem with defining PI is that you have at least 3 different
representations of pi: float/double/long double/complex/

What would be the best representation?
And using which rounding mode?

The best thing is to define it yourself and arbitrarily decide that you
want the constant as a (say) long double.


#define PI 3.1415926535897932384626433832795L

jacob
 
K

Kenneth Brody

Szabolcs said:
i've just found out (searching through n1124.pdf) that in c99 math.h
does not contain M_PI

what is the desired way to use the PI constant in a c code then?

#define PI 3.1415926535897932384626433832795
#define PI (4*atan(1))
...
?

In addition to Jacob's points, you should also consider this:

Do you want to define PI as a constant, or as something that
has to be calculated at runtime every time you use it?

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
M

Martin Ambuhl

Szabolcs said:
i've just found out (searching through n1124.pdf) that in c99 math.h
does not contain M_PI

what is the desired way to use the PI constant in a c code then?

#define PI 3.1415926535897932384626433832795
#define PI (4*atan(1))
....
?

There are two obvious disadvantages to the (4*atan(1)) form
a) atan(1) is not computed at compile time, so the computation of
atan(1) occurs each time PI is used
b) atan() returns a double. Your first form, which has more digits than
most implementations have for a long double, suggests an desire for
a degree of accuracy beyond the requirements even of astrophysics.
If LDBL_DIG > DBL_DIG you may well find (4*atan(1)) the less
satisfactory in terms of precision.

Suppose you ran a program like this:

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

#define PI_F 3.1415926535897932384626433832795f
#define PI_D 3.1415926535897932384626433832795
#define PI_L 3.1415926535897932384626433832795l
#define PI_C (4*atan(1))

int main(void)
{
printf("printf PI_x as double:\n");
printf("PI_F %.*g\n", DBL_DIG, PI_F);
printf("PI_D %.*g\n", DBL_DIG, PI_D);
printf("PI_L %.*g\n", DBL_DIG, (double) PI_L);
printf("PI_C %.*g\n\n", DBL_DIG, PI_C);


printf("printf PI_x as long double:\n");
printf("PI_F %.*Lg\n", LDBL_DIG, (long double) PI_F);
printf("PI_D %.*Lg\n", LDBL_DIG, (long double) PI_D);
printf("PI_L %.*Lg\n", LDBL_DIG, PI_L);
printf("PI_C %.*Lg\n\n", LDBL_DIG, (long double) PI_C);

return 0;
}

The output for one implementation
printf PI_x as double:
PI_F 3.14159274101257
PI_D 3.14159265358979
PI_L 3.14159265358979
PI_C 3.14159265358979

printf PI_x as long double:
PI_F 3.14159274101257324
PI_D 3.14159265358979312
PI_L 3.14159265358979324
PI_C 3.14159265358979312


suggests for this implementation:
a) if float operations are faster than doubles, and no more 8
significant digits are needed
#define PI 3.14159265f
suffices
b) if double operations are at least as fast as floats, and no
more than 16 significant digits, or, whatever the relative
speed of floats and doubles, if at 9 but not more than 16
significant digits are needed
#define PI 3.141592653589793
is preferred
c) If the maximum possibel precision is required with a standard
type, use
#define PI 3.1415926535897932384626433832795l
is best.

In fact each of those defines should probably carry all the digits up to
some unneeded level of precision, since a different implementation might
well make use of more than does this implementation.

The only reason I can see for
> #define PI (4*atan(1))
is that the implementation may well ensure that this has the maximum
precision and accuracy that a double can attain (but no more).
But that can probably be done with
#define PI 3.1415926535897932384626433832795 /* double */
better.
If you insist on using the (4*atan(1)) form, use, rather than a define,
in each function that now uses the symbol PI
const double PI = 4 * atan(1);
Straining the usual conventions on identifiers that are all caps.
 
K

Keith Thompson

jacob navia said:
The best thing is to define it yourself and arbitrarily decide that you
want the constant as a (say) long double.


#define PI 3.1415926535897932384626433832795L

This has about 102 significant bits of precision; it can silently fail
if long double happens to have a bigger mantissa than that. In most
applications, 32 digits is more than enough, but if it does matter,
it's going to be difficult to track down.

If you really need huge precision, and you need it to adapt to the
precision supported by your implementation, you can use 4*atan(1) --
or, better, 4*atanl(1) -- and arrange your code so it's only evaluated
once.

Or you can define PI as a macro, as above, and fail during compilation
if long double has more precision than you've specified:

#include <float.h>
#define PI 3.1415926535897932384626433832795L
#if LDBL_DIG > 32
#error "Definition of PI doesn't use the full precision of long double"
#endif

Or, for most applications, you can just define PI with as many digits
as you're going to need.
 
J

J. J. Farrell

There are two obvious disadvantages to the (4*atan(1)) form
a) atan(1) is not computed at compile time, so the computation of
atan(1) occurs each time PI is used

Would a conforming program be able to tell if it had been computed at
compile time? I'm not aware of any reason why a good compiler couldn't
do it at compile time.
 
K

Keith Thompson

J. J. Farrell said:
Would a conforming program be able to tell if it had been computed at
compile time? I'm not aware of any reason why a good compiler couldn't
do it at compile time.

No, a conforming program can't tell, as long as the compile-time
computation produces the same result as a run-time computation would
have.

At least one popular C compiler actually does this computation at
compilation time, even without any command-line options to enable
optimization.

This produces some interesting behavior. If the computation is done
at compilation time, the math library is not required. If it needs to
be done at run time, the math library is required, and the program
won't link without an extra option.

<OT>gcc, "-lm"</OT>
 
C

CBFalconer

J. J. Farrell said:
.... snip ...

Would a conforming program be able to tell if it had been computed
at compile time? I'm not aware of any reason why a good compiler
couldn't do it at compile time.

gcc does.
 
J

jacob navia

CBFalconer said:
gcc does.

And that can be quite WRONG, specially if you want
to redefine atan() to fix a broken implementation
for instance...

I am not sure all this "clever" optimizations are justified
in this case since this constant is known since ages with great
precision.

Why make it simple when you can do it complicated?

That's seems to be the motto here.
 
F

Flash Gordon

jacob navia wrote, On 06/07/07 22:35:
And that can be quite WRONG, specially if you want
to redefine atan() to fix a broken implementation
for instance...

Then you can't because the standard does not permit you to redefine
atan. You have to write your own myatan instead, or get a better
implementation.
I am not sure all this "clever" optimizations are justified
in this case since this constant is known since ages with great
precision.

That sounds like an excellent reason *to* optimise it. As the value is
know with great precision why bother recalculating it?
Why make it simple when you can do it complicated?

What is complicated about having a list of functions which, if called
with constants, can be replaced by doing the call at compile time and
inserting the result?
That's seems to be the motto here.

No, the motto is why make the compiled code slow when you can speed it up.

Note that sometimes some of the apparently pointless optimisations (ones
where you would never write code like that) get to speed things up
because of what earlier optimisations have done. Sometimes things like
autoconf can also lead to expressions which might have been variable at
run time ending up as compile time constants.
 
U

user923005

[...]
The best thing is to define it yourself and arbitrarily decide that you
want the constant as a (say) long double.
#define PI 3.1415926535897932384626433832795L

This has about 102 significant bits of precision; it can silently fail
if long double happens to have a bigger mantissa than that. In most
applications, 32 digits is more than enough, but if it does matter,
it's going to be difficult to track down.

If you really need huge precision, and you need it to adapt to the
precision supported by your implementation, you can use 4*atan(1) --
or, better, 4*atanl(1) -- and arrange your code so it's only evaluated
once.

Or you can define PI as a macro, as above, and fail during compilation
if long double has more precision than you've specified:

#include <float.h>
#define PI 3.1415926535897932384626433832795L
#if LDBL_DIG > 32
#error "Definition of PI doesn't use the full precision of long double"
#endif

Or, for most applications, you can just define PI with as many digits
as you're going to need.

/* Usually, using too many digits won't hurt a thing. */
/* CONST macro used so that you can easily substitute strings by
string pasting for bignum packages */

Red Hat Linux release 7.2 (Enigma)
Kernel 2.4.7-10 on an i686
login: builder
Password:
Last login: Fri Jul 6 15:31:09 from dcorbit64.corporate.connx.com
/home/builder> gcc -W -Wall -ansi -pedantic -std=c9x piconst.c
/home/builder> ./a.out
float precision pi = 3.141593
double precision pi = 3.141592653589793
long double precision pi = 3.141592653589793239
/home/builder> cat piconst.c
#define CONST(x) (x)
#define PI
CONST(3.141592653589793238462643383279502884197169399375105\
820974944592307816406286208998628034825342117067982148086513282306647L)

#include <stdio.h>
#include <float.h>
int main(void)
{
float fPi = PI;
double dPi = PI;
long double ldPi = PI;
printf("float precision pi = %.*f\n", FLT_DIG, fPi);
printf("double precision pi = %.*f\n", DBL_DIG, dPi);
printf("long double precision pi = %.*Lf\n", LDBL_DIG, ldPi);
return 0;
}
/home/builder>
 
K

Keith Thompson

jacob navia said:
And that can be quite WRONG, specially if you want
to redefine atan() to fix a broken implementation
for instance...

All identifiers with file scope in the standard headers, including
atan, are reserved for use as identifiers with external linkage (C99
7.1.3p1). If you define your own atan() function with external
linkage, you're invoking undefined behavior.

The standard reserves certain identifiers for good reasons.
I am not sure all this "clever" optimizations are justified
in this case since this constant is known since ages with great
precision.

Why make it simple when you can do it complicated?

That's seems to be the motto here.

I don't believe that gcc evaluates atan() at compilation time just to
make it easy to define pi. It does so for most or all math functions
(I've run into this for sqrt() as well). It's a perfectly ordinary
optimization.

As a programmer, of course, one probably shouldn't depend on 4*atan(1)
being evaluated at compilation time, since not all compilers will
perform that optimization -- though the penalty for making that
assumption is merely a loss of speed.
 
J

J. J. Farrell

And that can be quite WRONG,

How, if the end program can't tell the difference?
specially if you want
to redefine atan() to fix a broken implementation
for instance...

You have to change the implementation to do that, so either apply the
same fix in the compiler bit or disable the optimization.
 
S

Szabolcs Nagy

first thank you all for your answers

Martin said:
suggests for this implementation:
a) if float operations are faster than doubles, and no more 8
significant digits are needed
#define PI 3.14159265f
suffices
b) if double operations are at least as fast as floats, and no
more than 16 significant digits, or, whatever the relative
speed of floats and doubles, if at 9 but not more than 16
significant digits are needed
#define PI 3.141592653589793
is preferred
c) If the maximum possibel precision is required with a standard
type, use
#define PI 3.1415926535897932384626433832795l
is best.
let's suppose i use the c) alternative

float f,g;
....
g = f * PI;

will the multiplication be long double or float precision?
i thought the compiler would optimize it and only use floats.
const double PI = 4 * atan(1);
hmm what's the draw back of using
const long double PI = 3.14...L;
instead of
#define PI 3.14...L
 
A

Army1987

Would a conforming program be able to tell if it had been computed at
compile time? I'm not aware of any reason why a good compiler couldn't
do it at compile time.
But the compiler has to pretend that it is done at runtime. For
example, you can't initialize a static variable with it.
 
P

pete

Martin said:
There are two obvious disadvantages to the (4*atan(1)) form
a) atan(1) is not computed at compile time, so the computation of
atan(1) occurs each time PI is used
b) atan() returns a double. Your first form, which has more digits than
most implementations have for a long double, suggests an desire for
a degree of accuracy beyond the requirements even of astrophysics.
If LDBL_DIG > DBL_DIG you may well find (4*atan(1)) the less
satisfactory in terms of precision.

Suppose you ran a program like this:

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

#define PI_F 3.1415926535897932384626433832795f
#define PI_D 3.1415926535897932384626433832795
#define PI_L 3.1415926535897932384626433832795l
#define PI_C (4*atan(1))

int main(void)
{
printf("printf PI_x as double:\n");
printf("PI_F %.*g\n", DBL_DIG, PI_F);
printf("PI_D %.*g\n", DBL_DIG, PI_D);
printf("PI_L %.*g\n", DBL_DIG, (double) PI_L);
printf("PI_C %.*g\n\n", DBL_DIG, PI_C);

printf("printf PI_x as long double:\n");
printf("PI_F %.*Lg\n", LDBL_DIG, (long double) PI_F);
printf("PI_D %.*Lg\n", LDBL_DIG, (long double) PI_D);
printf("PI_L %.*Lg\n", LDBL_DIG, PI_L);
printf("PI_C %.*Lg\n\n", LDBL_DIG, (long double) PI_C);

return 0;
}

The output for one implementation
printf PI_x as double:
PI_F 3.14159274101257
PI_D 3.14159265358979
PI_L 3.14159265358979
PI_C 3.14159265358979

printf PI_x as long double:
PI_F 3.14159274101257324
PI_D 3.14159265358979312
PI_L 3.14159265358979324
PI_C 3.14159265358979312

suggests for this implementation:
a) if float operations are faster than doubles, and no more 8
significant digits are needed
#define PI 3.14159265f
suffices
b) if double operations are at least as fast as floats, and no
more than 16 significant digits, or, whatever the relative
speed of floats and doubles, if at 9 but not more than 16
significant digits are needed
#define PI 3.141592653589793
is preferred
c) If the maximum possibel precision is required with a standard
type, use
#define PI 3.1415926535897932384626433832795l
is best.

Pi can be calculated for any standard type.

/* BEGIN new.c */

#include <stdio.h>
#include <float.h>

long double fs_pil(void)
{
long unsigned n;
long double a, b;
static long double p;
static int initialized;

if (!initialized) {
initialized = 1;
n = 1;
a = 3;
do {
a /= 9;
b = a / n;
n += 2;
a /= 9;
b -= a / n;
n += 2;
p += b;
} while (b > LDBL_EPSILON / 4);
n = 1;
a = 2;
do {
a /= 4;
b = a / n;
n += 2;
a /= 4;
b -= a / n;
n += 2;
p += b;
} while (b > LDBL_EPSILON / 2);
p *= 4;
}
return p;
}

int main(void)
{
printf("fs_pil() - 3.1415926535897932384626433832795L is %Le\n",
fs_pil() - 3.1415926535897932384626433832795L);
return 0;
}

/* END new.c */
 
B

Barry Schwarz

first thank you all for your answers


let's suppose i use the c) alternative

float f,g;
...
g = f * PI;

will the multiplication be long double or float precision?
i thought the compiler would optimize it and only use floats.

Assuming you assign a value to f, it will be converted to long double,
the multiplication will be performed, and the result will be converted
to float. Look up arithmetic promotions in your reference of choice.
hmm what's the draw back of using
const long double PI = 3.14...L;
instead of
#define PI 3.14...L

1. You could not use PI where a compile time constant would be
needed.

2. Certain calculations would be deferred until execution time. For
example, in the statement
c = 2 * PI * r;
with the #define, the compiler could computer 2 * PI and use that
value in the generated code which would involve only one multiply
during program execution. With the const, I think most compilers will
generate two multiplies in the executable code.

3. Unless you define PI at file scope, it will only be visible in the
block it is defined in. The #define is "visible" for the rest of the
translation unit and, if you put it in a header, for as many units as
you want.

On the other hand, with the const, your debugger would show you the
variable PI just like it does any other.


Remove del for email
 
S

santosh

Army1987 said:
But the compiler has to pretend that it is done at runtime. For
example, you can't initialize a static variable with it.

Yes. That's why gcc emits a diagnostic when compiling such a construct
under conforming mode.
 
J

J. J. Farrell

But the compiler has to pretend that it is done at runtime. For
example, you can't initialize a static variable with it.

Obviously; if the C code contains a call to atan(1) the compiler has
to behave in the way prescribed by the C standard for when the code
contains a call to atan(1). How the compiler actually implements that
call is its business.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,434
Messages
2,571,691
Members
48,796
Latest member
Greg L.

Latest Threads

Top