Code Capsules

The Standard C Library, Part 3

Chuck Allison


This month I conclude this series on the Standard C Library by exploring the headers in Group III (see Table 1 through Table 3) .

Group III: For the "Complete" C Programmer

<float.h>

Perhaps nothing varies so widely across different computer environments as floating-point number systems. Some platforms require special hardware to make native floating-point instructions available. Others provide floating-point capability through software, and MS-DOS compilers must support both implementations.

A floating-point number system is a collection of numbers that can be expressed in scientific notation, with a fixed number of digits. To be precise, it is the finite set of numbers of the form ±0.d1d2...dp x be, where m < e < M. The parameters b, p, m, and M represent the radix, precision, minimum exponent, and maximum exponent, respectively. The header <float.h> provides manifest constants for these and other important floating-point parameters (see Table 4; also see the sidebar, "Floating-Point Number Systems"). As Table 4 illustrates, each implementation must support three potentially separate number systems, one each for float, double, and long double numbers.

<math.h>

Although C is not used widely in scientific programming, it provides a full-featured set of mathematical functions (see Table 6) . Most of these functions have the standard names used in mathematics, so if you know what they are, you know how to use them. A few deserve special mention, however. As the program in Listing 4 illustrates, the fmod function computes the remainder of dividing its first argument by its second. The answer to fmod(1234.56, 90.1234) is 62.9558 because

1234.56 - (13 * 90.1234) == 62.9558
The function modf stores the integer part of its first argument at the address given by its second argument, and returns the fractional part. frexp splits a floating-point number as given by its first argument into two parts, mantissa and base-2 exponent. In Listing 4, frexp computes a normalized fraction w and an integer p such that its first argument, y, is equivalent to

w x 2p
frexp's inverse, ldexp, returns a floating-point number given mantissa w and exponent p.

The floor of a number is itself if that number is an integer; otherwise the floor of a number is the next adjacent integer to the "left" on the number line, so the following relationships are valid:

    floor(90.1234) == 90
    floor(-90.1234) == -91
The ceiling of a number is the next integer to the right, so

    ceil(90.1234) == 91
    ceil(-90.1234) == -90
The calculator program in Listing 5 illustrates a number of <math.h> functions.

<errno.h>

<errno.h> implements a simple error reporting facility. It defines a global integer, errno, to hold certain error codes that are generated by a number of library functions. The C Standard requires vendors to provide only two codes, EDOM and ERANGE (see Table 7) . EDOM indicates a domain error, which usually means that you passed bad arguments to the function. For example, the sqrt function in <math. h> complains if you ask for the square root of a negative number. Math functions can set errno to ERANGE to indicate a range error, which mean that a calculation on valid arguments would result in an arithmetic overflow or underflow. Most of the mathematical functions in the standard library use errno to report such errors. As the calculator in Listing 5 illustrates, you should set errno to zero before invoking any function that uses this facility, and then check it immediately after the function returns. The function perror prints its string argument followed by a colon, followed by a string representation of the last error recorded in errno. perror(s) is equivalent to the expression:

    printf("%s: %s\n",s,strerror(errno));
strerror, defined in <string.h>, returns implementation-defined text that corresponds to errno.

The following functions from other standard library headers also set errno upon failure: strod, strtol, strtoul, fgetpos, fsetpos, and signal. The C Standard allows an implementation to provide error codes of its own, beyond EDOM and ERANGE, and to use the errno facility with other functions, but such use is of course non-portable.

<locale.h>

A locale in Standard C is a collection of preferences for the processing and display of information that is sensitive to culture, language, or national origin, such as date and monetary formats. The Standard recognizes five categories of locale-specific information, named by macros in <locale.h> (see Table 8) . Each of these categories can be set to a different locale (e.g., "american", or "italian"). For want of a better term, I call the collection of settings for all five categories the locale profile.

Standard C specifies two functions that deal with locales directly:

    struct lconv *localeconv(void);
    char *setlocale(int category, char *locale);
The members of the lconv structure appear in Listing 6. localeconv returns a static lconv object containing current settings for LC_MONETARY and LC_NUMERIC, and setlocale changes the locale for the given category to that specified in its second argument. You can set all categories to the given locale by specifying a category of LC_ALL (see Listing 7) . If locale is NULL, setlocale returns the current locale string for the category. All implementations must support the minimalist "C" locale, and a native locale named by the empty string (which may be the same as the "C" locale). Unfortunately, few U.S. vendors provide any additional locale support.

<setjmp.h)

When you encounter an exceptional condition deep within a set of nested function calls, you need a "super goto" that branches to a safe point higher up in the function call stack. That's what the setjmp/longjmp mechanism is for. You mark that safe return point with setjmp, and branch to it with longjmp. Here's the syntax:

   #include <setjmp.h>

   jmp_buf recover;

   main()
   {

      volatile int i = 0;
      for(;;)
      {
         if (setjmp(recover) != 0)
         {
            /* Recover from error in f() */
         }

         /* Get deeply nested... */
      }
      return 0;
   }

   . . .

   void f()
   {
      /* Do some risky stuff */

      if (<things go crazy>)
         longjmp(recover,1);

      /* else carry on */
   }
A jmp_buf (jump buffer) is an array that holds the system information necessary to restore execution at the setjmp point. For obvious reasons, a jump buffer must typically be global. When you call setjmp, the system stores the calling environment parameters, such as the contents of the stack and instruction pointer registers, in recover. setjmp always returns a zero when called directly. A call to longjmp restores the calling environment, so execution continues back at the setjmp call point, with one difference: it appears as if setjmp has returned the second argument from the longjmp call, in this case a 1. (One small quirk: if you give longjmp a second argument of zero, it returns a 1 anyway. Zero is the only argument with this behavior.) Since a longjmp performs an alternate return from a function, it interrupts the normal flow of a program, so you should use it only to handle unusual conditions.

When longjmp is called, the function containing the setjmp target must still be active (i.e., must not yet have returned to its own caller), otherwise the calling environment will no longer be valid. And of course, it is a bad idea to have more than one setjmp target with the same jmp_buf variable. Since calling environments typically involve registers, and since a compiler is free to store automatic variables in registers, you have no idea if an automatic object will have the correct value when you return from a longjmp. To get around this problem, you should declare automatics in any function containing a setjmp with the volatile qualifier, which guarantees that the inner workings of longjmp will leave them undisturbed. For a more detailed example of the setjmp/longjmp mechanism, see Listing 9 in "Code Capsules, File Processing, Part 2," CUJ, June 1993.

<signal.h>

A signal occurs when an unusual event interrupts the normal execution of a program, such as a divide-by-zero error or when the user presses an attention key, such as Control-C or DEL. The header <signal. h> defines six "standard signals," shown in Table 9. These signals originated on the PDP architecture under UNIX, so some of them may not apply to your environment. An implementation may also define other signals, or may ignore signals altogether, so signal handling is inherently non-portable.

Signals come in two flavors: synchronous and asynchronous. A synchronous signal is one that your program raises, such as dividing by zero, overflowing during a floating-point operation, or issuing a call to the abort function. You can also raise a signal explicitly with a call to raise, for example:

    raise(SIGABRT);
An asynchronous signal occurs as a result of events that your program can't foresee, such as a user pressing the attention key.

The default response to most signals is usually to abort the program, but you can either arrange for signals to be ignored or provide your own custom signal handlers. The following statement from Listing 8

    signal(SIGINT,SIG_IGN);
tells the environment to ignore any keyboard interrupt requests (Control-C on my machine). The keyboard input process still echoes the ^C token on the display, but it does not pass control to the default signal-handling logic that terminates the program. The statement

    signal(SIGINT,SIG_DFL)
restores the default behavior, so you can halt the program from the keyboard.

For more on signal handling, see Listing 11 - Listing 13 in the Code Capsule "Control Structures," in the June 1994 issue of CUJ.

<stdarg.h>

This header provides a facility for defining functions with variable-length argument lists, like printf's (see Table 10) . printf's function prototype in your compiler's stdio.h should look something like

    int printf(const char *, ...);
The ellipsis tells the compiler to allow zero or more arguments of any type to follow the first argument in a call to printf. For printf to behave correctly, the arguments that follow the format string must match the types of the corresponding edit descriptors in the format string. If there are fewer arguments than the format string expects, the result is undefined. If there are more arguments, they are ignored. The bottom line is that when you use the ellipsis in a function prototype you are telling the compiler not to type-check your optional arguments because you think you know what you're doing — so be sure that you do.

The program in Listing 9 shows how to use the va_list mechanism to find the largest integer in a variable-length argument list. For more on <stdarg.h>, see the Code Capsule, "Variable-length Argument Lists" in the Feb. 1994 issue of CUJ.

Conclusion

In this three-part series I have attempted to convey the overall flavor of the Standard C library, especially in the functionality it provides. It is foolish to waste time reinventing this functionality. Although I have classified the library into three groups of decreasing priority, my priorities may not match yours. If you make your living programming in C or C++, you will do well to master the entire library. Enough said.

Floating-point Number Systems

The computer's difficulty in manipulating real numbers has long been a source of frustration and confusion for students and practitioners alike. The program in Listing 1 shows how a simple sequence of sums can go awry. It calculates the expression ex by the formula:

Click Here for Equation

and compares the result to the "correct" answer given by the exp function in the standard library. This works correctly for positive arguments, but the result for negative arguments isn't even close! The problem, of course, is that computers, with their finite capabilities, can only represent a minuscule subset of the set of real numbers. A good chunk of numerical analysis deals with roundoff error, the quirks of finite-precision arithmetic.

In days gone by, many computers used a fixed-point number system, which uses numbers derived from a given radix (b), precision (p), and fraction size (f). For example, the values b=10, p=4, and f=1 define the following set of 19,999 numbers:

F = {-999.9, - 999.8,...,999.8, 999.9}
Since these numbers are evenly spaced, the maximum absolute error in representing any real number x in this system is bounded by 0.05. In other words,

 |x-fix(x)| < 0.05
The set of machine integers form a fixed-point system with f=0, and a maximum absolute error of 0.5 for systems that round, and no greater than 1.0 for systems that truncate.

Absolute error is not generally useful in mathematical computations, however. As is often the case, you are more often interested in percentages, or how one number differs in relation to another. You compute the relative error of y with respect to x by the following formula:

Click Here for Equation

Consider how the numbers 865.54 and 0.86554 are represented in F:

fix(865.54) = 865.5
fix(.86554) = 0.9
Because the number of decimals is fixed, the second is a much poorer fit than the first, which the relative errors illustrate:

Click Here for Equation

The second is 1,000 times worse than the first!

Nowadays computers provide floating-point number systems, which represent numbers in the form

±0.d1d2...dp X be
where

m < e < M, 0 < di < b
This is like the scientific notation that you learned in school, except that in this case the base (b) can be other than 10, and there is an upper limit on the precision (p). Most floating-point systems use a normalized representation, which means that d1 cannot be zero. These number systems are called floating-point for the obvious reason — the radix point "floats" and the exponent (e) adjusts accordingly to preserve the correct value.

Consider a floating-point system G defined by b=10, p=4, m=-2, and M=3. The numbers from the fixed-point example above are represented as:

fl(8.65.54)+0.8655 x 103
fl(865.554)=0.8655 x 100
Now look what happens when calculating the relative errors of the two representations:

Click Here for Equation

Click Here for Equation

It can be shown that the relative error of representing any real number within the range of a floating-point system is no greater than b1-p, which is 0.001 in G.

Floating-point numbers are not evenly spaced. In G, for example, the next number greater than 1 is 1.001, a spacing of 0.001, but the next number after 10 is 10.01, which is 0.01 to the right. In general, the spacing among all numbers between powers of b, say between be and be+1, is be+1-p. A trivial combinatorial analysis shows that each interval [be, be+1] has (b-1) bp-1 floating-point numbers, and the total number of representable floating-point numbers in a system is 2(M-m+1)(b-1)bp-1 (so increasing p increases the density of the number system). And as shown above, although smaller numbers are closer together and larger numbers are farther apart, the relative spacing between consecutive floating-point numbers is essentially the same throughout the system. In fact, the relative spacing between two adjacent representable numbers within an interval [be, be+1] is b1-p, and between be+1 and its immediate predecessor is b-p.

The quantity b1-p, which also happens to be the spacing between 1.0 and its immediate successor, is called the machine epsilon (e), and is a good measure of the granularity of a floating number system. The smallest floating-point magnitude other than zero is of course b, usually denoted as s, and the largest magnitude, l, is bM(1-b-p).

The Standard C header <float.h> provides manifest constants for all of these parameters as follows:

b == FLT_RADIX
p == DBL_MANT_DIG
m == DBL_MIN_EXP
M == DBL_MAX_EXP
e == DBL_EPSILON
s == DBL_MIN
l == DBL_MAX
<float.h> also provides the float and long double equivalents of the last five parameters, representing the three (not necessarily distinct) floating-point number systems in standard C (see Table 4) .

The program in Listing 2 calculates a root of x2+x+1 in the interval [-1, 1] by the method of bisection. The algorithm halves the interval (which must contain a sign change for the function F) until the relative spacing between the endpoints is less than or equal to machine epsilon. Such a loop may never terminate if you expect greater precision than this.

Should you find yourself in an environment that does not provide the parameters in <float.h>, you can compute b, p, and e directly. To see how this is possible, remember that the spacing between consecutive floating-point numbers increases with the magnitude of the numbers. Eventually there is a point where the spacing between adjacent numbers is greater than 1. (In fact, the first interval where this holds true is [bp, bp+1], because the integers therein require p+1 digits in their representation.) The positive integers precisely representable in a floating-point system are these:

1,2,...,bp-1,bp,bp+b,bp+2b,..., bp+1,bp+1+b2,...
To find the radix, the program in Listing 3 keeps doubling a until it reaches a point where the spacing exceeds 1. It then finds the next larger floating-point number and subtracts a to get b. The program then computes the smallest power of b that will produce numbers whose adjacent spacing exceeds 1 that power is the precision p. To find e, the program finds the nearest representable neighbor to the right of 1, and subtracts 1.

Table 1 Standard C Headers: Group I (required knowledge for every C programmer)

<ctype.h>   Character Handling
<stdio.h>   Input/Output
<stdlib.h>  Miscellaneous Utilities
<string.h>  Text Processing

Table 2 Standard C Headers: Group 11 (tools for the professional)

<assert.h>  Assertion Support for Defensive Programming
<limits.h>  System Parameters for Integer Arithmetic
<stddef.h>  Universal Types & Constant
<time.h>    Time Processing

Table 3 Standard C Headers: Group III (power at your fingertips when you need it)

<errno.h>   Error Detection
<float.h>   System Parameters for Real Arithmetic
<locale.h>  Cultural Adaptation
<math.h>    Mathematical Functions
<setjmp.h>  Non-local Branching
<signal.h>  Interrupt Handling (sort of)
<stdarg.h>  Variable-length Argument Lists

Table 4 Definitions in <float.h>

Parameter                 Meaning                "Minimum"
                                                   Value
--------------------------------------------------------------
FLT_RADIX        exponent base                   2
FLT_ROUNDS       rounding mode                   (see Table 5)

FLT_MANT_DIG     precision for float
DBL_MANT_DIG     precision for double
LDBL_MANT_DIG    precision for long double
FLT_DIG          base-10 precision for float     6
DBL_DIG          same for double                 10
LDBL_DIG         same for long double            10
FLT_MIN_EXP      min. exponent for float
DBL_MIN_EXP      same for double
LDBL_MIN_EXP     same for long double
FLT_MIN_10_EXP   min. base-10 float exponent     -37
DBL_MIN_10_EXP   same for double                 -37
LDBL_MIN_10_EXP  same for long double            -37
FLT_MIN          smallest float                  1E-37
DBL_MIN          smallest double                 1E-37
LDBL_MIN         smallest long double            1E-37
FLT_MAX_EXP      max. exponent for float
DBL_MAX_EXP      same for double
LDBL_MAX_EXP     same for long double
FLT_MAX_10_EXP   max. base-10 float exponent     +37
DBL_MAX_10_EXP   same for double                 +37
LDBL_MAX_10_EXP  same for long double            +37
FLT_MAX          largest float                   1E+37
DBL_MAX          largest double                  1E+37
LDBL_MAX         largest long double             1E+37
FLT_EPSILON      machine epsilon for float       1E-5
DBL_EPSILON      same for double                 1E-9
LDBL_EPSILON     same for long double            1E-9

Table 5 Values for FLT_Rounds

-1  indeterminable
 0  toward zero
 1  to nearest
 2  toward positive infinity
 3  toward negative infinity

Table 6 Functions defined in <math.h>

acos   arc-osine
asin   arc-sine
atan   arc-tangent (principal value)
atan2  arc-tangent (full circle)
ceil   ceiling
cos    cosine
cosh   hyperbolic cosine
exp    power of e
fabs   absolute value
floor  floor (greatest-integer-in
       function)
fmod   modulus (remainder)
frexp  normalized fraction/
       exponent parts
ldexp  inverse of frexp
log    natural logarithm
log10  logarithm base 10
modf   integer/fractional parts
pow    raise a number to a power
sin    sine
sinh   hyperbolic sine
sqrt   square root
tan    tangent
tanh   hyperbolic tangent

Table 7 Definitions in <errno.h>

errno   Global integer to report
        certain errors
EDOM    Domain error code
ERANGE  Range error code

Table 8 Locale Categories

Category                              Functionality
----------------------------------------------------------------------
LC_COLLATE   Adapts strcoll, strxfrm, wcscoll, and wcsxfrm to the
             language/culture of the locale.
LC_CTYPE     Adapts the isxxx/iswxxx functions in ctype.h to the
             character set associated with the locale, and affects
             multibyte/wide character mapping.
LC_MONETARY  Sets parameters pertaining to the display of monetary
             values such as decimal point, grouping (e.g., thousands),
             group separator, etc. This category is purely advisory and
             affects no standard library functions.
LC_NUMERIC   Like LC_MONETARY, but for non-monetary values (e.g., the
             decimal point may be different than for non-monetary
             values).
LC_TIME      Adapts strftime and wcsftime to cultural specifications.

Table 9 Definitions in <signal.h>

Signal Macros
-----------------------------------------------
SIGABRT  abnormal termination
         (raised by abort)
SIGFPE   computational exception
         (e.g., overflow)
SIGILL   invalid function image
         (e.g., illegal instruction)
SIGINT   interactive attention (e.g.,
         Control-C)
SIGSEGV  attempt to access protected
         memory
SIGTERM  termination request

Functions
-----------------------------------------------
raise    generates a signal from
         within a program
signal   registers a function as a
         signal-handler

Table 10 Definitions in <stdarg.h>

Type
---------------------------------------------
va_list   A variable-length argument list

Macros
-----------------------------------------------
va_start  Initializes a va_list
va_arg    Gets next arg in a va_list
va_end    Closes a va_list

Listing 1 Shows roundoff error in computing powers of e

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

double e(double x);

main()
{
   printf("e(55.5) == %g, exp(55.5) == %g\n",
         e(55.5), exp(55.5));
   printf("e(-55.5) == %g, exp(-55.5) == %g\n",
         e(-55.5), exp(-55.5));
   printf("1/e(55.5) == %g\n",1.0 / e(55.5));
   return 0;
}

double e(double x)
{
   double sum1 = 1.0;
   double sum2 = 1.0 + x;
   double term = x;
   int i = 1;

   /* Calculate exp(x) via Taylor Series */
   while (sum1 != sum2)
   {
      sum1 = sum2;
      term = term * x / ++i;
      sum2 += term;
   }
   return sum2;
}

/* Output:
e(55.5) == 1.26866e+24, exp(55.5) == 1.26866e+24
e(-55.5) == -6.76351e+06, exp(-55.5) == 7.88236e-25
1/e(55.5) == 7.88236e-25
*/

/* End of File */

Listing 2 Illustrates use of machine epsilon in a root-finding algorithm

/* root.c:
 *
 *   To use a different precision, change ftype
 *   and the suffix of the floating-point constants
 */

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

#define sign(x) ((x < 0.0) ? -1 : (x > 0.0) ? 1 : 0)

#define PREC DBL_DIG
#define EPS DBL_EPSILON

typedef double ftype;

ftype root(ftype a, ftype b, ftype (*f)(ftype))
{

   ftype fofa = f(a);
   ftype fofb = f(b);
   assert(a < b);
   assert(fofa * fofb < 0.0);

   /* Close-in on root via bisection */
   while (fabs(b - a) > EPS*fabs(a))
   {
      ftype x = a + (b-a)/2.0;
      ftype fofx = f(x);
      if (x <= a || x >= b || fofx == 0.0)
         return x;
      if (sign(fofx) == sign(fofa))
      {
         a = x;
         fofa = fofx;
      }
      else
      {
         b = x;
         fofb = fofx;
      }
   }
   return a;
}

main()
{
   extern ftype f(ftype);
   printf("root == %.*f\n",PREC,root(-1.0,1.0,f));
   return 0;
}

ftype f(ftype x)
{
   return x*x + x - 1.0;
}

/* Output:
root == 0.618033988749895
*/
/* End of File */

Listing 3 Computes Machine Floating-point Parameters

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

main()
{
   int beta, p;
   double a, b, eps, epsp1, sigma, nums;

   /* Discover radix */
   a = 1.0;
   do
   {
      a = 2.0 * a;
      b = a + 1.0;
   } while ((b - a) == 1.0);

   b = 1.0;
   do
      b = 2.0 * b;
   while ((a + b) == a);
   beta = (int) ((a + b) - a);
   printf("radix:\n");
   printf("\talgorithm: %d\n",beta);
   printf("\tprovided: %d\n",FLT_RADIX);

   /* Compute precision in bits */
   p = 0;
   a = 1.0;
   do
   {
      ++p;
      a *= (double) beta;
      b = a + 1.0;
   } while ((b - a) == 1.0);
   printf("precision:\n");
   printf("\talgorithm: %d\n",p);
   printf("\tprovided: %d\n",DBL_MANT_DIG);

   /* Compute machine epsilon */
   eps = 1.0;
   do
   {
      eps = 0.5 * eps;
      epsp1 = eps + 1.0;
   } while (epsp1 > 1.0);
   epsp1 = 2.0 * eps + 1.0;
   eps = epsp1 - 1.0;
   printf("machine epsilon:\n");
   printf("\talgorithm: %g\n",eps);
   printf("\tformula: %g\n",pow(FLT_RADIX,1.0-DBL_MANT_DIG));
   printf("\tprovided: %g\n",DBL_EPSILON);

   /* Compute smallest normalized magnitude */
   printf("smallest non-zero magnitude:\n");
   printf("\tformula: %g\n",pow(FLT_RADIX,DBL_MIN_EXP-1));
   printf("\tprovided: %g\n",DBL_MIN);

   /* Compute larget normalized magnitude */
   printf("largest non-zero magnitude:\n");
   printf("\tformula: %g\n",
         pow(FLT_RADIX,DBL_MAX_EXP-1) * FLT_RADIX *
         (1.0 - pow(FLT_RADIX,-DBL_MANT_DIG)));
   printf("\tprovided: %g\n",DBL_MAX);

   printf("smallest exponent: %d\n",DBL_MIN_EXP);
   printf("largest exponent: %d\n",DBL_MAX_EXP);

   nums = 2 * (FLT_RADIX - 1)
       * pow(FLT_RADIX,DBL_MANT_DIG-1)
       * (DBL_MAX_EXP - DBL_MIN_EXP + 1);
   printf("This system has %g numbers\n",nums);
   return 0;
}

/* Output (Borland C++):
radix:
   algorithm: 2
   provided: 2
precision:
   algorithm: 53
   provided: 53
machine epsilon:
   algorithm: 2.22045e-16
   formula: 2.22045e-16
   provided: 2.22045e-16
smallest non-zero magnitude:
   formula: 2.22507e-308
   provided: 2.22507e-308
largest non-zero magnitude:
   formula: 1.79769e+308
   provided: 1.79769e+308
smallest exponent: -1021
largest exponent: 1024
This system has 1.84287e+19 numbers
*/
/* End of File */

Listing 4 Illustrates several<math.h> functions

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

main()
{
   double x = 1234.56, y = 90.1234, z, w;
   int p;

   printf("x == %g, y == %g\n",x,y);
   printf("fmod(x,y) == %g\n",fmod(x,y));
   printf("floor(y) == %g\n",floor(y));
   printf("ceil(y) == %g\n",ceil(y));
   w = modf(y,&z);
   printf("after modf(y,&z): w == %g, z == %g\n",w,z);
   w = frexp(y,&p);
   printf("after frexp(y,&p): w == %g, p == %d\n",w,p);
   printf("ldexp(w,p) == %g\n",ldexp(w,p));
   return 0;
}

/* Output:
x == 1234.56, y == 90.1234
fmod(x,y) == 62.9558
floor(y) == 90
ceil(y) == 91
after modf(y,&z): w == 0.1234, z == 90
after frexp(y,&p): w == 0.704089, p == 7
ldexp(w,p) == 90.1234
*/
/* End of File */

Listing 5 A simple calculator that illustrates some <math.h> functions

/* calc.c: Lame-brain Calculator-
 *
 *  For simplicity in parsing, this program
 *  reads lines of the form:
 *
 *     value operation
 *
 *  where the value is optional in some cases.
 *  For example, the following script computes
 *  the integer part of sqrt(1 + 3.4*3.4):
 *
 *      3.4 =
 *      3.4 *
 *      1 +
 *      sqrt
 *      floor
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#define LINSIZ 40

char *getline(char *);

main()
{
   double  reg = 0.0;
   char    line[LINSIZ];
   while (getline(line) != NULL)
   {
      char *op;
      double val;

      /* Parse command string */
      val = strtod(line,&op);
      while (isspace(*op))
         ++op;
      strupr(op);

      /* Perform operation */
      errno = 0;
      if (*op == '+')
         reg += val;
      else if (*op == '-')
         reg -= val;
      else if (*op == '*')
         reg *= val;
      else if (*op == '/')
       {
         if (val != 0)
            reg /= val;
         else
         {
            puts("ERROR>>> invalid divisor");
            continue;
         }
      }
      else if (*op == '=')
         reg = val;
      else if (*op == '^')
      {
         if (val == 0.0)
            reg = 1.0;
         else if (val == 0.5)
            reg = sqrt(reg);
         else
            reg = pow(reg,val);
      }
      else if (strncmp(op,"NEGATE",1) == 0)
         reg = -reg;
      else if (strncmp(op,"MOD",1) == 0)
      {
         if (val == 0.0)
         {
            puts("ERROR>>> invalid modulus");
            continue;
         }
         else
            reg = fmod(reg,val);
      }
      else if (strncmp(op,"CEIL",1) == 0)
         reg = ceil(reg);
      else if (strncmp(op,"FLOOR",1) == 0)
         reg = floor(reg);
      else if (strncmp(op,"ROUND",1) == 0)
         reg = (reg < 0.0) ? ceil(reg - 0.5)
                        : floor(reg + 0.5);
      else if (strncmp(op,"SQRT",1) == 0)
         reg = sqrt(reg);
      else if (strncmp(op,"QUIT",1) == 0)
         exit(0);
      else if (*op != '\0')
      {
         puts("ERROR>>> invalid operation");
         continue;
      }

      if (errno)
         perror("ERROR>>>");
      else
         printf("\t%s => %g\n",line,reg);
   }
   return 0;
}

char *getline(char *buf)
{
   fputs ("Calc> ",stdout);
   fflush(stdout);
   return gets(buf);
}

/* Output:
Calc> 3.4 =
       3.4 = => 3.4
Calc> 3.4 *
       3.4 * => 11.56
Calc> 1 +
       1+ => 12.56
Calc> sqrt
       SQRT => 3.54401
Calc> floor
       FLOOR => 3
Calc> q
*/

/* End of file */

Listing 6 The members of struct lconv

struct lconv
{
   char *decimal_point;
   char *thousands_sep;
   char *grouping;
   char *int_curr_symbol;
   char *currency_symbol;
   char *mon_decimal_point;
   char *mon_thousands_sep;
   char *mon_grouping;
   char *positive_sign;
   char *negative_sign;
   char int_frac_digits;
   char frac_digits;
   char p_cs_precedes;
   char p_sep_by_space;
   char n_cs_precedes;
   char n_sep_by_space;
   char p_sign_posn;
   char n_sign_posn;
};

Listing 7 Shows the effect of locale settings on the decimal point character and time formatting

/*  tlocale.c: Illustrates locales-
 *
 *      Compiled in Visual C++ under Windows NT 3.5
 */

#include <locale.h>
#include <stdio.h>
#include <time.h>

void print_stuff(void);

main()
{
   /* First in the C locale */
   puts("In the C locale:");
   print_stuff();

   /* Now try German */
   puts("\nIn the German locale:");
   setlocale(LC_ALL,"german");
   print_stuff();

   /* Now try American */
   puts("\nIn the American locale:");
   setlocale(LC_ALL,"american");
   print_stuff();

   /* Now try Italian */
   puts("\nIn the Italian locale:");
   setlocale(LC_ALL,"italian");
   print_stuff();
   return 0;
}

void print_stuff(void)
{
   char text[81];
   time_t timer = time(NULL);
   struct lconv *p = localeconv();

   printf("decimal pt. == %s\n",
         p->decimal_point);
   printf("currency symbol == %s\n",
         p->int_curr_symbol);
   printf("%.2f\n",l.2);
   strftime(text,sizeof text,"%A, %B, %d,
           %Y (%x)\n", localtime(&timer));
   puts(text);
}

In the C locale:
decimal pt. == ,
currency symbol ==
1.20
Tuesday, January 03, 1995 (01/03/95)


In the German locale:
decimal pt. == .
currency symbol == DEM
1,20
Dienstag, Januar 03, 1995 (03.01.95)


In the American locale:
decimal pt. == .
currency symbol == USD
1.20
Tuesday, January 03, 1995 (01/03/95)


In the Italian locale:
decimal pt. == ,
currency symbol == ITL
1,20
marted, gennaio 03, 1995 (03/01/95)
/* End of File */

Listing 8 Shows how to register a signal

/* ignore.c: Ignores interactive attention
key as */
#include <stdio.h>
#include <signal.h>

main()
{
   char buf[BUFSIZ];
   int i;

   /* Ignore keyboard interruptions */
   signal(SIGINT,SIG_IGN);

   while (gets(buf))
      puts(buf);

   /* Restore default attention key handling
      so the user can abort form the keyboard */
   signal(SIGINT,SIG_DFL);
   for (i = 1; ; ++i)
      printf("%d%c",i,(i%15 ? ' ' : '\n'));
}

/* Sample Execution
c:>ignore
hello
hello
^C
there
there
^C
^Z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
46 45 48 49 50 51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 ^C
*/
/* End of file*/

Listing 9 Uses the <stdarg.h> macros to search a variable-length list of integers

/* max.c */
#include <stdio.h>
#include <stdarg.h>

int maxn(size_t count, ...)
{
   int n, big;
   va_list numbers;

   va_start(numbers,count);

   big = va_arg(numbers,int);
   while (count--)
   {
      n = va_arg(numbers,int);
      if (n > big)
         big = n;
   }

   va_end(numbers);
   return big;
}

main()
{
   printf("max = %d\n",maxn(3,1,3,2));
   return 0;
}

/* Output:
max = 3
*/

/* End of File */