Skip to content

C

C is a general-purpose, procedural computer programming language

"C is not a big language, and it is not well served by a big book." - Brian W. Kernighan, Dennis M. Ritchie

Hello, World

  1. This is the canonical example of a C program:

    c
    // hello.c
    
    #include <stdio.h>
    
    int main(void) {
        printf("Hello, World!");
    }
  2. Compile the program using gcc or clang:

    bash
    gcc -o hello hello.c
    # or
    clang -o hello hello.c
  3. Run the program:

    bash
    ./hello

NOTE

If you get an error like : permission denied: ./hello in Linux/Unix, then run chmod +x hello to make the file executable

Syntax of C

The syntax of C is based on the syntax of the B language, which was developed by Ken Thompson in 1970 at Bell Labs. B was a simplified version of the BCPL language developed by Martin Richards in 1966. BCPL was a typeless language, but B introduced types

  • C is a case-sensitive language (Play, play, and PLAY are different)
  • C programs are written in a text editor and saved with the .c extension
  • C programs are compiled using a compiler like gcc or clang or any other C compiler

Comments

Comments will be completely ignored by the compiler:

c
/* hello world program
 * multi-line comments
 */

//  single line comment

Reserved Words

Reserved words in C cannot be used as identifiers (variable names, function names, etc.)

  • There are 32 keywords in C
text
auto        double  int         struct
break       else    long        switch
case        enum    register    typedef
char        extern  return      union
const       float   short       unsigned
continue    for     signed      void
default     goto    sizeof      volatile
do          if      static      while

Structure of the Code

In C, main() function should only have variable declarations and function calls

Example:

c
/* comment
 *
 * Converts distances from miles to kilometres.
 */

#include <stdio.h> // preprocessor directive: printf, scanf definitions
//         ^ static header file

#define KMS_PER_MILE 1.609 // preprocessor directive: conversion constant
// ^ constant macro
//           ^ constant identifier

int        // return type
main(void) // function name
{
  double miles, // distance in miles
  // ^ data type
      kms;      // equivalent distance in kilometres
  //   ^ variable identifier

  // Get the distance in miles.
  printf("Enter the distance in miles> ");
  scanf("%lf", &miles);
  // ^ standard identifier

  // Convert the distance to kilometers.
  kms = KMS_PER_MILE * miles;
  //  ^              ^ operators

  // Display the distance in kilometers.
  printf("That equals %f kilometers.\n", kms);

  return (0); // return statement
  // ^ reserved word
}

Compiling and Linking

C/C++ programs consist of source files and headers. Source files and headers are usually text files, but need not be

  • Much of the text in C/C++ source and header files represents declarations

    • The declarations establish the existence of entities such as functions, namespaces, objects, templates, types, and values
  • C/C++ has no specific rules about which declarations must go into source files and which must go into headers

  • For a function, we typically:

    • declare it in a header, and...
    • define it in a corresponding source file
  • However, for a function that's inline, constexpr, or consteval, then:

    • define it in a header file

Steps:

text
Source code (.c) file
      |
      v
.---------------.
| Pre-processor | <-------- Header files
.---------------.
      |
      | <-------- expanded code
      |
      v
.---------------.
| Compiler      |
.---------------.
      |
      .--------------.
                     |
                     v
          Assembly code (.s) file
                     |
      .--------------.
      |
      v
.---------------.
| Assembler     |
.---------------.
      |
      .--------------.
                     |
                     v
          Object code (.o) file
                     |
      .--------------.
      |
      v
.---------------.
| Linker        | <-------- Libraries
.---------------.
      |
      v
Executable file
  1. Pre-processor:

    • Flag: -E to Pre-process output to stdout
    • Strip out comments
    • Anything that starts with pound sign, or "octothorpe", (#) is something the preprocessor operates on
    • #include: This C Preprocessor tells compiler to pull the contents of another file and insert it into the code right there
    • #define: This C Preprocessor tells compiler to replace all instances of a certain string with another string
    • Replaces all the macros with the actual code
    • <stdio.h>: It is known as a header file (they don't get compiled?)
    • TODO: Check if source files can be generated like hello.i
  2. Compiler: Compiler produce assembly code, machine code, or whatever anything based on options

    • Flag: -S to generate assembly code

    • .s file is an assembly file or Intermediate Representation (IR) code

    • Solution Configuration: Rules and configurations to build the project

    • Solution Platforms: Platform that is being targeted

    • Only C/C++ files are complied not Header files

    • Every C/C++ file is complied individually into a respective Object file

    • Liker: Stitches all these Object file into an executable file

    • Compiler compiles a C++ file if it only contains function declaration without and definition and is used inside that file.

    • C++ files are called Translation Units (Files have no meaning to C++ Compiler)

  3. Assembler: Translate assembly code to object file

    • Flag: -c to compile only
    • .o file is an object file
    • It is not an executable file
    • It is the code in machine language (binary) that the computer can understand
  4. Linker: Combines object files into an executable file

    • Flag: -o to specify output file name
    • Linking all the source files together, that is all the other object codes in the project.
    • Linking function calls with their definitions. The linker knows where to look for the function definitions in the static libraries or dynamic libraries
  • Running gcc or clang without any flags will compile and link the code and produce an executable file (a.out)

  • Use the Compiler flag -save-temps to save temporary files (like .i, .s, .o)

  • Some compilers like clang produce an intermediate representation (IR) ("pseudo-assembly") code before the assembly code (file extension: .ll)

    • To generate the IR code, use the flag -emit-llvm like clang -emit-llvm -S hello.c

Example Program

Let's say we are writing a add program, which is split into two files:

  • main.c: Contains the main function and uses methods from add.c

  • add.c: Contains the add function

  • Create add.c and write the add function:

    c
    // add.c
    int add(int a, int b)
    {
        return a + b;
    }
  • Compile the add file to object file:

    bash
    gcc -c add.c
    
    # output: add.o
  • Now, create main.c and use the add function in the main file:

    c
    // main.c
    #include <stdio.h>
    
    int main()
    {
        printf("Sum: %d\n", add(10, 20));
        return 0;
    }
  • Compile the main file to object file:

    bash
    gcc -c main.c
    
    # output:
    # ./main.c:6:25: error: implicit declaration of function ‘add’
  • The compiler is not able to find the add function because it is in a different file, so we need let the compiler know about the add function by defining the function prototype in the main file:

    c
    // main.c
    #include <stdio.h>
    
    int add(int a, int b);
    
    int main()
    {
        printf("Sum: %d\n", add(10, 20));
        return 0;
    }
  • Compile the main file again:

    bash
    gcc -c main.c
    
    # output: main.o
  • Create the executable file:

    bash
    gcc -o main main.o
    
    # output:
    # main.c:(.text+0x1a): undefined reference to `add'
  • The linker is not able to find the add function, so we need to link the add.o file with the main.o file:

    bash
    gcc -o main main.o add.o
    
    # output: main
  • We have successfully compiled and linked the program. Now, run the executable file:

    bash
    ./main
    
    # output: Sum: 30

In the above example we added the function prototype in the main file which helps the compiler to know about the add function. But if need to use the add function in multiple files, then we have to write the function prototype in all the files. To avoid this, we can create a header file and include it in all the files:

  • Create a header file add.h and write the function prototype:

    c
    // add.h
    int add(int a, int b);
  • Include the header file in the main file, or any other file where you want to use the add function:

    c
    // main.c
    #include <stdio.h>
    #include "add.h"
    
    int main()
    {
        printf("Sum: %d\n", add(10, 20));
        return 0;
    }

We can represent the dependencies between the files in a diagram called a dependency graph

Runtime Bounds Checking

Checks for buffer overflows and underflows at runtime

bash
gcc -o test test.c -fsanitize=address -fsanitize=bounds

Compiler Flags

Compiler flags are used to specify the behaviour of the compiler and the output that is generated

Common Compiler Flags:

  • -std=c99: Use the C99 standard, or -std=c2x for the C2x standard (latest that the compiler supports)

    • gnu17: default in GCC and Clang
  • -o: Output file name

  • -E: Pre-process output to stdout

  • -S: Generate assembly code

  • -c: Generate object file

  • -l: Link with library (like -l m for math.h library)

  • -g: Debugging information

  • -v: Display the programs invoked by the compiler

  • -Wall: Enable all warnings

  • -Werror: Treat warnings as errors

  • -Wextra: Enable extra warnings

  • -fsanitize=address: Address Sanitizer

  • -Wdocumentation: Warn about issues in documentation comments

  • -pedantic: Issue all warnings demanded by strict ISO C and ISO C++

  • -O0: No optimization (default)

  • -O1: Optimize

  • -O2: More optimization

  • -O3: Even more optimization

  • -O4: All optimization

  • -march=native: Optimize for the current machine

  • -ffast-math: Assume no NaNs or Infs

  • --save-temps: Save temporary files (like .i, .s, .o)

Headers and Namespaces

  • Pre-processor statements:

    • #pragma once: Include the file only once in the compilation process
    • Header guards: #ifndef, #define, #endif
    c
    // sum.h
    #pragma once
    
    int sum(int a, int b);
    
    // or for older compilers
    #ifndef SUM_H
    #define SUM_H
    
    int sum(int a, int b);
    
    #endif
  • Namespace:

    cpp
    // sum.h
    #pragma once
    
    namespace customSum {
        int sum(int a, int b);
    }
    
    // sum.cpp
    namespace customSum {
        int sum(int a, int b)
        {
          return a + b;
        }
    }
    
    
    // Main file
    #include "sum.h"
    
    int main()
    {
        cout << customSum::sum(10, 20);
    
        return 0;
    }

"Include hell" is a term used to describe a situation where a project has a large number of dependencies, and each dependency has its own dependencies, and so on. This can lead to a situation where a single source file can include hundreds of header files

Dynamic type using auto

Maps are like JavaScript objects.

Unity Build

Unity build is a technique where you include all your source files into a single file and compile that file. This can speed up the compilation process because the compiler can see all the code at once and optimize it better

It is also known as a "single compilation unit" or "jumbo build"

c
// unity.c
#include "file1.c"
#include "file2.c"
#include "file3.c"

Address Sanitizer (ASAN)

ASAN is a runtime memory error detector for C/C++ programs. It finds:

  • Use after free (dangling pointer dereference)
  • Heap buffer overflow
  • Stack buffer overflow
  • Global buffer overflow
  • Use after return
  • Use after scope
  • Initialization order bugs
  • Memory leaks

AddressSanitizer

Memory

The main memory can divided into:

  1. Heap:

  2. Stack:

  3. Code Section:

Example:

c
int main()
{
    int A[5];
    int B[5]={2,4,6,8,10};
    int i;
    for(i=0;i<5;i++)
    {
        printf("%d", B[i]);
    }
}
// ARRAYS A AND B WILL APPEAR IN THE STACK AFTER DECLARATION.

Data Types

A type defines a set of possible values and a set of operations that can be performed on those values

  • An object is some memory that holds a value of a given type

  • A value is a set of bits in memory interpreted according to a type

  • A variable is a named identifier that refers to a value in memory

  • A declaration is a statement that introduces an identifier (name) and describes its type, be it a type or a function (describing its signature: return type, name, and parameters)

    • The compiler uses the declaration to determine how much memory to allocate for the identifier, how to interpret the bits stored there, and to check that the operations performed on the identifier are valid
    • It can be done multiple times
    • It can be done without initialization
    • It does not allocate memory

    This thing exists somewhere

    c
    int a; // declaration: int is the type, a is the identifier
    char c; // declaration: char is the type, c is the identifier
    
    int add(int a, int b); // declaration: int is the return type,
                           // add is the identifier, int a, int b are the parameters
  • A definition actually instantiates/implements the identifier, the compiler asks memory manager to set aside memory for that value or function

    • It can be done only once
    • The function definition includes the function body

    This thing exists here; make memory for it

    c
    int a = 10; // definition: int is the type, a is the identifier, 10 is the value
    char c = 'A'; // definition: char is the type, c is the identifier, 'A' is the value
    
    int add(int a, int b) // definition: int is the return type,
    {                     // add is the identifier, int a, int b are the parameters
        return a + b;     // function body
    }
  • An initialization is a declaration with an initial value (definition + definition)

    • It can be done only once
    • It allocates memory and assigns a value

    Here is the initial value for this thing

    c
    int a = 10; // initialization: int is the type, a is the identifier, 10 is the value
    char c = 'A'; // initialization: char is the type, c is the identifier, 'A' is the value
  • A constant is a value that cannot be changed

    • It is a literal value
    • It is a compile-time value
    c
    const int a = 10; // constant: int is the type, a is the identifier, 10 is the value
    
    #define PI 3.14 // constant: PI is the identifier, 3.14 is the value
  • A literal is a value that appears directly in the code

    • It is a compile-time value
    c
    int a = 10; // 10 is a literal
    char c = 'A'; // 'A' is a literal

If a variable is declared but not initialized, then it will contain a garbage value (whatever was in that memory location before)

  • Compilers can initialize variables to zero, but it is not guaranteed

The size of the data types is compiler dependent especially before C99 standard, but after C99 standard new data types are introduced which have fixed sizes (like int32_t, int64_t, etc.)

NOTE

If something is declared but not defined, then the linker doesn't know what to link references to and complains about a missing symbols. If you define something more than once, then the linker doesn't know which of the definitions to link references to and complains about duplicated symbols

Character

Character data types are used to store characters (letters, digits, symbols)

  • char keyword is used to declare a character variable
  • It is 1 byte in size
  • A ' (single quote) is used to represent an ASCII character (like 'A', 'B', '1', '2', '!', '@', etc.)
  • Numbers from 0 to 127 can also be stored in a char variable

String Representation

In C, a string is represented as an array of characters terminated by a null character (\0 ASCII value 0)

  • A string is stored in a contiguous memory location
  • C dose not have a built-in string data type
c
char name[6] = {'J', 'o', 'h', 'n', '\0'};
char name[] = "John";

There are two ways to represent a string in C:

  1. Using a character array:

    • A string is represented as an array of characters terminated by a null character (\0 ASCII value 0)
    c
    char name[6] = {'J', 'o', 'h', 'n', '\0'};
    char name[] = "John";
  2. Using a character pointer:

    • A string is stored in a contiguous memory location
    c
    char *name = "John";

The difference between a character array and a character pointer is that a character array is a fixed-size memory location, whereas a character pointer is a variable-size memory location

Integer

Integer data types are used to store whole numbers (positive or negative)

Data TypeRange
short-32,768 .. 32,767
unsigned short0 .. 65,535
int-2,147,483,648 .. 2,147,483,647
unsigned0 .. 4,294,967,295
long-2,147,483,648 .. 2,147,483,647
unsigned long0 .. 4,294,967,295

New data types introduced in C99 standard:

  • Better data types for fixed-width integers using stdint.h:

    • Like size of int is not fixed, it depends on the compiler/target (it should be at least 16 bits)
    C89C99WindowsLinux
    signed charint8_t
    long intint32_tint64_t
    long longint64_t
    unsigned shortuint16_t
    charAny size
    • int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t
    • uintptr_t ensures that the variable is large enough to hold a pointer
    • Constants like INT32_MAX

Floating Point Numbers

Floating-point data types are used to store real numbers (positive or negative)

Data TypeSignificat DigitsRange (approx.)
float6±3.4E-38 .. ±3.4E+38
double15±1.7E-308 .. ±1.7E+308
long double19±3.4E-4932 .. ±1.1E+4932

Numerical Precision

When working with floating-point numbers, the operations are not always exact and can have some loss of accuracy or round-off errors (or representational error) due to the way floating-point numbers are stored in memory (binary representation)

  • Precision: The number of digits that can be stored in a floating-point number
  • Accuracy: The closeness of the measured value to the true value
c
#include <stdio.h>

int main()
{
    float a = 1.0;
    float b = 10.0;
    float c = a / b;

    printf("%.10f\n", c); // 0.1000000015

    return 0;
}

In the above example, the result of a / b is 0.1000000015 instead of 0.1 due to the loss of accuracy in floating-point numbers (round-off error)

  • When a very large number is added to a very small number, the small number is lost due to the limited precision of floating-point numbers. It is called catastrophic cancellation (or cancelation error)

    • To avoid this, use a tolerance value when comparing floating-point numbers (like 1e-6) instead of exact equality
  • When two very small numbers are multiplied, the result may be too small to be represented accurately. It will be represented as 0, which is called arithmetic underflow

  • When two very large numbers are multiplied, the result may be too large to be represented accurately. It will be represented as infinity, which is called arithmetic overflow

  • When a number is divided by 0, the result is INFINITY (present in math.h) (printed as inf), which is called division by zero (or singularity) error

Void

The void data type is used to specify that a function does not return a value or a pointer that does not point to any data type (like void *)

  • It is used to specify that a function does not take any parameters
c
void printMessage()
{
    printf("Hello, World!");
}

// use `void` to specify that the function does not take any parameters
// it is recommended to use `void` in the parameter list
void printMessageWithVoid(void)
{
    printf("Hello, World!");
}

int main()
{
    void *ptr;

    printMessage(25); // warning: Too many arguments in call to 'print_hello' [warn_call_wrong_number_of_arguments]

    printMessageWithVoid(25); // error: Too many arguments to function call, expected 0, have 1 [typecheck_call_too_many_args]
    return 0;
}

Arrays

Definition: Contiguous area of memory consisting of equal-size elements

  • Declared with size inside square brackets []

  • It can be declared without size, but in this case it must be initialized with items. The size of the array will be equal to the number of items

  • If number of items are less than the declared size of an array, the rest of the places will be filled with 0

  • If an array is declared and never initialized then it will contain garbage values

Example:

c
#include <stdio.h>

int main()
{
    int A[5] = {1, 2, 3, 4, 5};
    int B[] = {1, 2, 3};        // SIZE 3
    int C[5] = {1, 2, 3};       // {1,2,3,0,0}
    int D[2];                   // {3213, 234324}

    for (int i = 0; i < 5; i++)
    {
        printf("%d\n", A[i]);
    }
    printf("Completed");

    return 0;
}

Struct (Structure)

Definition: It's a physically grouped list of dissimilar data items under one name in a block of memory, allowing the different data items to be accessed via a single pointer. It's used for defining user-defined data types, apart from the primitive data types

  • Group of related data items
  • struct is the keyword used to define a structure
  • . (dot operator) is used to access the members of the structure
  • Its size will the sum of sizes consumed by all of its elements
  • Structure Padding is used to allocate memory for a structure

Example: Define a struct called Rectangle

c
// DEFINITION
struct Rectangle
{
  int length;
  int breadth;
} r1, r2; // GLOBAL STRUCT VARIABLES

int main()
{
  // DECLARATION (MEMORY IS ALLOCATED)
  struct Rectangle r;
  struct Rectangle r1={10,5};

  // INITIALISATION
  r.length=25;
  printf("Area of the Rectangle is %d", r.length * r.breadth);
}
c
struct Card
{
  int face;
  int shape;
  int color;
};

int main()
{
  struct Card deck[52]={{1,0,0}, {0,0,1}, .... };
  deck[0].face=2;
}
  • struct do not have member functions

Directives

Directives are commands to the compiler that start with a # symbol and are processed before the actual compilation of the program

  • #include: Includes a file in the program (like #include <stdio.h> for a standard library or #include "file.h" for a user-defined file)

  • #define: Defines a macro (a name that represents a value)

    • It notifies the preprocessor to replace all instances of the identifier by the value

Standard Functions and Libraries

printf

printf is a standard library function in C that prints formatted output to the standard output stream (stdout)

  • It is defined in the stdio.h header file

  • It returns the number of characters printed (excluding the null byte)

  • It is a variadic function, which means it can take a variable number of arguments

  • The format string is a string that contains format specifiers, Format specifiers start with a % symbol, The format specifiers are replaced by the values of the arguments

  • The format specifiers are:

    • %d: Integer
    • %f: Float
    • %c: Character
    • %s: String
    • %p: Pointer
    • %x: Hexadecimal
    • %zu: sizeof value
  • The format string can contain escape sequences:

    • \n (newline)
    • \t (tab)
    • \\ (backslash)
    • \" (double quote)
    • \' (single quote)
  • The format string can contain width specifiers:

    • %5d (5 characters wide)
    • %10.2f (10 characters wide with 2 decimal places)
  • The format string can contain flags:

    • + (always show sign)
    • - (left-justify)
    • 0 (pad with zeros)
    • # (alternate form)
    • space (space if positive)
  • The format string can contain length modifiers:

    • h (short)
    • l (long)
    • ll (long long)
    • j (intmax_t)
    • z (size_t)
    • t (ptrdiff_t)
    • L (long double)
  • The format string can contain conversion specifiers:

    • d (decimal)
    • i (integer)
    • o (octal)
    • u (unsigned decimal)
    • x (hexadecimal)
    • X (uppercase hexadecimal)
    • f (float)
    • e (scientific notation)
    • E (scientific notation)
    • g (shortest representation)
    • G (shortest representation)
    • a (hexadecimal float)
    • A (hexadecimal float)
    • c (character)
    • s (string)
    • p (pointer)
    • n (number of characters written so far)
    • % (percent sign)
c
#include <stdio.h>

int main()
{
    int a = 10;
    float b = 20.5;
    char c = 'A';
    char s[] = "Hello, World!";
    void *p = &a;

    printf("Integer: %d\n", a);
    printf("Float: %f\n", b);
    printf("Character: %c\n", c);
    printf("String: %s\n", s);
    printf("Pointer: %p\n", p);
    printf("Hexadecimal: %x\n", a);

    return 0;
}
PlaceholderTypeFunction Use
%ccharprintf/scanf
%dintprintf/scanf
%fdoubleprintf
%lfdoublescanf

Versions

  1. 1972: First release

  2. 1978: K&R C

  3. 1989: C89 (ANSI C)

    • Most of the C code written today is based on the ANSI C standard
    • Almost every C compiler available today is ANSI C compliant
    • Every platform/target
    • Can be compiled with a C++ compiler (with no or minimal changes)
  4. 1999: C99

    • Added several new features to the C language
    • Standardized // comments
    • Local variable declarations:
    c
    // C89
    int main(void) {
      // All variables must be declared at the beginning of the block
      int i;
      for (i = 0; i < 10; i++) {
        printf("%d", i);
      }
      // i is still in scope here
    }
    
    
    // C99
    int main(void) {
      for (int i = 0; i < 10; i++) {
        printf("%d", i);
      }
      // i is not in scope here
    }
    • Initializing structure members:
    c
    typedef struct {
      int id;
      int age;
      char* name;
    } User;
    
    // C89
    // Order of initialization must match the order of the structure members
    // You can't skip any member
    User rick = {
      557,
      30,
      "Rick"
    };
    
    // C99: Designated initializers
    // You can initialize the structure members in any order
    // You can skip any member
    User rick = {
      .name = "Rick",
      .id = 557,
      .age = 30
    };
    • Better data types for fixed-width integers using stdint.h, like int32_t, int64_t, etc.

    • Compound literals:

      • A compound literal is an unnamed object that is created on the fly
      • It is a way to create an object of a structure or array type without giving it a name
      • It is useful when you need to pass a structure
    c
    // C89
    Point point = {1, 2};
    draw_point(point);
    
    // C99
    draw_point((Point){1, 2});
  5. 2011: C11

  6. 2018: C17

  7. 2024: C23

Modern C Features

Code Style

Naming Conventions

Code Formatting

Using clang-format, you can format your code:

bash
clang-format -style=llvm -dump-config > .clang-format

Different styles of writing C code:

c
// Allman
while (x == y)
{
  func1();
  func2();
}

// Kernighan & Ritchie
while (x == y) {
  func1();
  func2();
}

// GNU
while (x == y)
  {
    func1 ();
    func2 ();
  }

// Whitesmiths
while (x == y)
    {
    func1();
    func2();
    }

// Horstmann
while (x == y)
{
  func1();
  func2();
}

// Haskell style
while (x == y)
  { func1()
  ; func2()
  ;
  }

// Ratliff style
while (x == y) {
    func1();
    func2();
    }

// Lisp style
while (x == y)
  { func1();
    func2(); }

Memory Leaks

c
#include <stdlib.h>
#include <string.h>

Floating Point Numbers

IEEE 754: Floating point number specification

  • It is a compression algorithm
BitsPrecision
16 bitHalf Precision
32 bitSingle Precision
64 bitDouble Precision
128 bitQuadruple Precision
256 bitOctuple Precision

Floating point numbers in 16 Bit System:

  • The floating point number's binary representation is split into 3-parts:
  1. Sign (1 Bit):

    • Number is Positive or Negative
  2. Exponent (5 Bits):

    • Represents a range
    Exponent (n)Power Range (2^n)Numerical Range
    0[0, 1][1, 2]
    1[1, 2][2, 4]
    2[2, 3][4, 8]
    3[3, 4][8, 16]
    4[4, 5][16, 32]
  3. Mantissa (10 Bits):

    • (Number - lower bound) / (upper bound - lower bound)
  • For 64 Bit system:

    • Sign (1 Bit)
    • Exponent (11 Bits)
    • Mantissa (52 Bits)

Formula that represents a floating point number:

  • N = (-1)^sign * 1.mantissa * 2^(exponent - 15)

According to IEEE 754 spec:

  • -0 if sign=1, exponent=00000, and mantissa=0000000000

  • INFINITY if exponent=11111 and mantissa=0000000000

    • -INFINITY if sign=1
  • NaN if exponent=11111 and mantissa=someValue

    • There are man NaN
  • 2^0: de-normalized number

Example:

text
The number: 25.1 (decimal) is stored as:

- Sign:       0       (Positive)
- Exponent:   131     (1000 0011)
- Mantissa:   4771021 (1001 0001 1001 1001 1001 101)

Value actually stored in float:   25.1000003814697265625
Error due to conversion:          0.0000003814697265625
Binary Representation:            0100 0001 1100 1000 1100 1100 1100 1101
Hexadecimal Representation:       41C8CCCD

References:

References

Documentation: If you're on a Unix system then run man 3 printf