Back to C Programming
2026-01-137 min read

Numeric limits

Learn Numeric limits step by step with clear examples and exercises.

Why This Matters

Understanding numeric limits is crucial for several reasons:

  1. Avoiding Overflow and Underflow: Numeric limits help us understand the maximum and minimum values that a data type can hold. This knowledge helps prevent overflow (when the value exceeds the maximum limit) and underflow (when the value falls below the minimum limit), which can lead to unexpected behavior or program crashes.
  1. Debugging: Numeric limits can help you identify and debug issues in your code more efficiently by providing a clear understanding of the range within which variables should be operated.
  1. Performance Optimization: By knowing the numeric limits, you can optimize your code to use appropriate data types for specific tasks, ensuring efficient memory usage and faster execution times.
  1. Understanding Floating-Point Precision: Numeric limits also help in understanding the precision of floating-point numbers, which is essential when working with mathematical operations that require high accuracy.
  1. Handling Special Values: Understanding numeric limits helps in handling special values like NaN (Not-a-Number), positive and negative infinity, and subnormal numbers, which can lead to unexpected behavior if not handled properly.

Prerequisites

Before diving into numeric limits, you should have a good understanding of:

  1. C programming basics, including variables, operators, and control structures.
  2. Data types in C, such as char, int, float, and double.
  3. Basic input/output operations using functions like scanf() and printf().
  4. Understanding of floating-point representation, such as IEEE 754 standard.
  5. Familiarity with mathematical concepts like exponentiation, logarithms, and trigonometric functions.

Core Concept

Characteristics of Integer Types

In C, the numeric limits for integer types are defined in the `` header file. The table below lists some essential characteristics of core language integer types:

| Type | Bit Width | Minimum Value | Maximum Value |

|--------|------------|---------------|---------------|

| char | CHAR_BIT | SCHAR_MIN | SCHAR_MAX |

| signed char | SCHAR_WIDTH | SCHAR_MIN | SCHAR_MAX |

| short | SHRT_WIDTH | SHRT_MIN | SHRT_MAX |

| int | INT_WIDTH | INT_MIN | INT_MAX |

| long | LONG_WIDTH | LONG_MIN | LONG_MAX |

| long long | LLONG_WIDTH | LLONG_MIN | LLONG_MAX |

The bit width of each type indicates the number of bits used to represent its values. The minimum and maximum values are defined as macro constants in ``.

Characteristics and Special Values of Floating-Point Types

For floating-point types (float, double, and long double), the numeric limits are more complex due to their binary representation. The table below lists some essential characteristics and special values:

| Type | ROUNDS | EVAL_METHOD | INFINITY | NAN | FLT_SNAN |

|---------|----------|--------------|---------------|-----------------|------------------|

| float | FLT_ROUNDS | FLT_EVAL_METHOD | INFINITYF | NANf | FLT_SNAN_MIN_128|

| double| DBL_ROUNDS| DBL_EVAL_METHOD | INFINITY | NAN | DBL_SNAN_MIN_1024|

| long double| LDBL_ROUNDS| LDBL_EVAL_METHOD | INFINITYL | NANL | LDBL_SNAN_MIN_16384|

The FLT_ROUNDS, DBL_ROUNDS, and LDBL_ROUNDS variables specify the rounding direction for floating-point arithmetic. The FLT_EVAL_METHOD, DBL_EVAL_METHOD, and LDBL_EVAL_METHOD variables indicate the method used to evaluate certain expressions involving subnormal numbers or denormals.

The special values INFINITY, NAN, and FLT_SNAN (or their double and long double counterparts) represent positive infinity, a not-a-number (NaN) value, and a signaling NaN value, respectively.

Floating-Point Precision and Rounding Modes

The precision of floating-point numbers is determined by the number of bits used to represent the mantissa and exponent. For example:

  • A float has approximately 7 significant digits (23 bits for mantissa, 8 bits for exponent).
  • A double has approximately 15 significant digits (53 bits for mantissa, 11 bits for exponent).
  • A long double has approximately 19 significant digits (64 bits for mantissa, 15 bits for exponent).

The rounding mode determines how a floating-point operation is rounded to the nearest representable value. The rounding modes in C are defined as follows:

  • FE_TONEAREST: Round to the nearest representable number (default).
  • FE_TOWARDZERO: Round toward zero.
  • FE_UPWARD: Round away from zero (toward positive infinity).
  • FE_DOWNWARD: Round away from zero (toward negative infinity).

Worked Example

Let's consider a simple example demonstrating numeric limits and floating-point precision:

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

int main() {
printf("Maximum value of char: %d\n", CHAR_MAX);
printf("Maximum value of int: %d\n", INT_MAX);
printf("Maximum value of long: %ld\n", LONG_MAX);
printf("Maximum value of long long: %lld\n", LLONG_MAX);

float pi = 3.14159265358979323846f; // Approximate value of Pi as a float
double pi_double = 3.14159265358979323846; // Exact value of Pi as a double
long double pi_long_double = 3.14159265358979323846L; // Exact value of Pi as a long double

printf("Approximate Pi (float): %f\n", pi);
printf("Exact Pi (double): %lf\n", pi_double);
printf("Exact Pi (long double): %Lf\n", pi_long_double);

float epsilon = 1.0e-5f; // Machine epsilon for float
double epsilon_double = 1.0e-15; // Machine epsilon for double
long double epsilon_long_double = 1.0e-19L; // Machine epsilon for long double

printf("Machine epsilon (float): %f\n", epsilon);
printf("Machine epsilon (double): %lf\n", epsilon_double);
printf("Machine epsilon (long double): %Lf\n", epsilon_long_double);

return 0;
}

In this example, we demonstrate the maximum values of various integer types and approximate and exact values for Pi using different floating-point types. The program then prints these values to the console along with machine epsilons for each type.

Common Mistakes

  1. Using the wrong data type: Using a data type with insufficient precision or range can lead to overflow, underflow, or other unexpected behavior. Always choose the appropriate data type for your needs and consider the floating-point precision when working with mathematical operations.
  1. Ignoring numeric limits in arithmetic operations: Performing arithmetic operations beyond the numeric limits of a data type can result in overflow or underflow. Be mindful of these limitations when designing algorithms and writing code.
  1. Not handling special floating-point values: Incorrectly handling NaN, negative zero, or infinite values can lead to unexpected behavior or incorrect results. Always check for these special cases in your code.
  1. Assuming floating-point operations are always exact: Floating-point operations are approximate due to their binary representation and finite precision. This can lead to errors when working with mathematical operations that require high accuracy.
  1. Not considering rounding modes: Different rounding modes can affect the results of floating-point arithmetic operations, so choosing the appropriate rounding mode is essential for accurate calculations.
  1. Neglecting to initialize floating-point variables: Floating-point variables should be initialized to avoid unexpected behavior or incorrect results due to uninitialized values.

Subheadings under Common Mistakes:

  • Handling Special Floating-Point Values
  • Rounding Modes and Their Impact on Results
  • Initializing Floating-Point Variables

Practice Questions

  1. Write a program that prints the minimum and maximum values of all integer types supported by C.
  2. Write a program that calculates the sum of all integers between 1 and INT_MAX using an appropriate data type.
  3. Write a program that checks whether a given floating-point number is NaN, positive infinity, or negative infinity.
  4. Write a program that demonstrates overflow and underflow for different data types.
  5. Write a program that calculates the square root of a large number using a long double to demonstrate better precision compared to other floating-point types.
  6. Write a program that demonstrates the effect of rounding modes on floating-point arithmetic operations.
  7. Write a program that calculates the machine epsilon for different floating-point types and compares them.
  8. Write a program that calculates Pi using the Gregory-Leibniz series, demonstrating the effect of precision when using different floating-point types.
  9. Write a program that calculates the sum of the first N numbers using an appropriate data type for large values of N.
  10. Write a program that calculates the factorial of a large number using a long double to demonstrate better precision compared to other floating-point types.

FAQ

  1. Why are numeric limits important? Numeric limits help us avoid overflows, underflows, and other unexpected behavior in our code by providing a clear understanding of the range within which variables should be operated. They also help in understanding the precision of floating-point numbers, which is essential when working with mathematical operations that require high accuracy.
  1. How can I find the numeric limits for my specific C compiler or platform? The `` header file defines the numeric limits for most common platforms. However, some compilers may provide additional headers or macros to define more specific numeric limits. You can also consult your compiler's documentation for detailed information.
  1. What is a denormal number, and why does it matter in floating-point arithmetic? A denormal number is a very small floating-point number with reduced precision. Denormals can cause issues when performing arithmetic operations, as they may be rounded to zero or generate subnormals, which have reduced accuracy.
  1. What is catastrophic cancellation, and how does it affect floating-point arithmetic? Catastrophic cancellation occurs when two nearly canceling numbers are subtracted, resulting in a significant loss of precision due to the rounding errors associated with floating-point arithmetic. This can lead to incorrect results or unexpected behavior in numerical calculations.
  1. Why do machine epsilons matter in numerical analysis? Machine epsilons help us understand the inherent errors in floating-point arithmetic due to finite precision. By knowing the machine epsilon, we can estimate the maximum relative error that can occur during calculations and adjust our algorithms accordingly.
  1. What is a subnormal number, and how does it affect floating-point arithmetic? A subnormal number is a very small positive floating-point number with reduced precision. Subnormals are used to represent numbers smaller than denormals, but they can still cause issues in arithmetic operations due to their reduced precision.
  1. What is the difference between a signaling NaN and a quiet NaN? A signaling NaN generates a floating-point exception when encountered in certain mathematical operations, while a quiet NaN does not. Quiet NaNs are more common and are used to represent undefined or invalid results.