| ## Safe Numerics

### Using Automatic Type Promotion

The C++ standard describes how binary operations on different integer types are handled. Here is a simplified version of the rules:

- promote any operand smaller than `int` to an `int` or `unsigned int`.

- if the size of the signed operand is larger than the size of the signed operand, the type of the result will be signed. Otherwise, the type of the result will be unsigned.

- Convert the type each operand to the type of the result, expanding the size as necessary.

- Perform the operation the two resultant operands.

So the type of the result of some binary operation may be different than the types of either or both of the original operands.

If the values are large, the result can exceed the size that the resulting integer type can hold. This is what we call "overflow". The C/C++ standard characterizes this as undefined behavior and leaves to compiler implementors the decision as to how such a situation will be handled. Usually, this means just truncating the result to fit into the result type - which sometimes will make the result arithmetically incorrect. However, depending on the compiler and compile time switch settings, such cases may result in some sort of run time exception or silently producing some arbitrary result.

The complete signature for a safe integer type is:

    template <
        class T,                  // underlying integer type
        class P = native,         // type promotion policy class
        class E = default_exception_policy // error handling policy class
    >
    safe;

The promotion rules for arithmetic operations are implemented in the default `native` type promotion policy are consistent with those of standard C++

Up until now, we've focused on detecting when an arithmetic error occurs and invoking an exception or other kind of error handler.

But now we look at another option. Using the `automatic` type promotion policy, we can change the rules of C++ arithmetic for safe types to something like the following:

- for any C++ numeric type, we know from `std::numeric_limits` what the maximum and minimum values that a variable can be - this defines a closed interval.

- For any binary operation on these types, we can calculate the interval of the result at compile time.

- From this interval we can select a new type which can be guaranteed to hold the result and use this for the calculation. This is more or less equivalent to the following code:

      int x, y;
      int z = x + y               // could overflow
      // so replace with the following:
      int x, y;
      long z = (long)x + (long)y; // can never overflow

  One could do this by editing his code manually as above, but such a task would be tedious, error prone, non-portable and leave the resulting code hard to read and verify. Using the `automatic` type promotion policy will achieve the equivalent result without these problems.

When using the `automatic` type promotion policy, with a given a binary operation, we silently promote the types of the operands to a wider result type so the result cannot overflow. This is a fundamental departure from the C++ Standard behavior.

If the interval of the result cannot be guaranteed to fit in the largest type that the machine can handle (usually 64 bits these days), the largest available integer type with the correct result sign is used. So even with our "automatic" type promotion scheme, it's still possible to overflow. So while our `automatic` type promotion policy might eliminate exceptions in our example above, it wouldn't be guaranteed to eliminate them for all programs.

Using the `loose_trap_policy` exception policy will produce a compile time error anytime it's possible for an error to occur.

This small example illustrates how to use automatic type promotion to eliminate all runtime penalty.

    //  Copyright (c) 2018 Robert Ramey
    //
    // Distributed under the Boost Software License, Version 1.0. (See
    // accompanying file LICENSE_1_0.txt or copy at
    // http://www.boost.org/LICENSE_1_0.txt)

    #include <iostream>

    #include <boost/safe_numerics/safe_integer.hpp>
    #include <boost/safe_numerics/exception_policies.hpp>
    #include <boost/safe_numerics/automatic.hpp>
    #include "safe_format.hpp" // prints out range and value of any type

    using safe_t = boost::safe_numerics::safe<
        int,
        boost::safe_numerics::automatic, // note use of "automatic" policy!!!
        boost::safe_numerics::loose_trap_policy
    >;

    int main(int, const char *[]){
        std::cout << "example 82:\n";
        safe_t x(INT_MAX);
        safe_t y = 2;
        std::cout << "x = " << safe_format(x) << std::endl;
        std::cout << "y = " << safe_format(y) << std::endl;
        std::cout << "x + y = " << safe_format(x + y) << std::endl;
        return 0;
    }

- the `automatic` type promotion policy has rendered the result of the sum of two `integers` as a `safe<long`> type.

- our program compiles without error - even when using the `loose_trap_policy` exception policy. This is because since a `long` can always hold the result of the sum of two integers.

- We do not need to use the `try/catch` idiom to handle arithmetic errors - we will have no exceptions.

- We only needed to change two lines of code to achieve our goal of guaranteed program correctness with no runtime penalty.

The above program produces the following output:

    example 82:
    x = <int>[-2147483648,2147483647] = 2147483647
    y = <int>[-2147483648,2147483647] = 2
    x + y = <long>[-4294967296,4294967294] = 2147483649

Note that if any time in the future we were to change safe<int> to safe<long long> the program could now overflow. But since we're using `loose_trap_policy` the modified program would fail to compile. At this point we'd have to alter our yet program again to eliminate run time penalty or set aside our goal of zero run time overhead and change the exception policy to `default_exception_policy` .

Note that once we use automatic type promotion, our programming language isn't C/C++ anymore. So don't be tempted to so something like the following:

    // DON'T DO THIS !
    #if defined(NDEBUG)
    using safe_t = boost::numeric::safe<
        int,
        boost::numeric::automatic, // note use of "automatic" policy!!!
        boost::numeric::loose_trap_policy
    >;
    #else
    using safe_t = boost::numeric::safe<int>;
    #endif