pre-boost Home Libraries People FAQ More

PrevUpHomeNext

Tutorial

Problem: Arithmetic operations can yield in correct results.
Problem: Undetected overflow
Problem: Implicit conversions change data values

Problem: Arithmetic operations can yield in correct results.

When some operation results in a result which exceeds the capacity of a data variable to hold it, the result is undefined. This is called "overflow". Since word size can differ between machines, code which produces correct results in one set of circumstances may fail when re-compiled on a machine with different hardware. When this occurs, Most C++ compilers will continue to execute with no indication that the results are wrong. It is the programmer's responsabiity to ensure such undefined behavior is avoided.

This program demonstrates this problem. The solution is to replace instances of char type with safe<char> type.

#include <cassert>
#include <stdexcept>
#include <iostream>

#include "../include/safe_integer.hpp"
//#include "../include/safe_compare.hpp"

void detected_msg(bool detected){
    std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
}

int main(int argc, const char * argv[]){
    std::cout << "example 1:";
    std::cout << "undetected erroneous expression evaluation" << std::endl;
    std::cout << "Not using safe numerics" << std::endl;
    try{
        char x = 127;
        char y = 2;
        char z;
        // this produces an invalid result !
        z = x + y;
        // it is the wrong result !!!
        assert(z != 129);
        // but assert fails to detect it since C++ implicitly
        // converts variables to int before evaluating he expression!
        assert(z != x + y);
        std::cout << static_cast<int>(z) << " != " << x + y << std::endl;
        detected_msg(false);
    }
    catch(...){
        assert(false); // never arrive here
    }
    // solution: replace char with safe<char>
    std::cout << "Using safe numerics" << std::endl;
    try{
        using namespace boost::numeric;
        safe<char> x = 127;
        safe<char> y = 2;
        safe<char> z;
        // rather than producing and invalid result an exception is thrown
        z = x + y;
        assert(false); // never arrive here
    }
    catch(std::range_error & e){
        // which can catch here
        std::cout << e.what() << std::endl;
        detected_msg(true);
    }
    return 0;
}

Problem: Undetected overflow

A variation of the above is when a value is incremented/decremented beyond it's domain. This is a common problem with for loops.

#include <cassert>
#include <stdexcept>
#include <iostream>

#include "../include/safe_integer.hpp"
//#include "../include/safe_compare.hpp"

void detected_msg(bool detected){
    std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
}

int main(int argc, const char * argv[]){
    std::cout << "example 3: ";
    std::cout << "implicit conversions change data values" << std::endl;
    std::cout << "Not using safe numerics" << std::endl;
    try{
        int x = -1000;
        // the following silently produces an incorrect result
        char y = x;
        detected_msg(false);
    }
    catch(...){
        assert(false); // never arrive here
    }
    // solution: replace int with safe<int> and char with safe<char>
    std::cout << "Using safe numerics" << std::endl;
    try{
        using namespace boost::numeric;
        safe<int> x = -1000;
        // throws exception when conversion change data value
        safe<char> y = x;
        assert(false); // never arrive here
    }
    catch(std::range_error & e){
        std::cout << e.what() << std::endl;
        detected_msg(true);
    }
    return 0;
}

Problem: Implicit conversions change data values

A simple assign or arithment expression will generally convert all the terms to the same type. Sometimes this can silently change values. For example, when a signed data variable contains a negative type, assigning to a unsigned type will be permitted by any C/C++ compiler but will be treated as large unsigned value. Most modern compilers will emit a compile time warning when this conversion is performed. The user may then decide to change some data types or apply a static_cast. This is less than satisfactory for two reasons:

  • It may be unwield to change all the types to signed or unsigned.

  • Litering one's program with static_cast makes it more difficult to read.

  • We may believe that our signed type will never contain a negative value. If we use a static_cast to suppress the warning, we'll fail to detect a program error when it is commited. This is aways a risk with casts.

This solution is the same as the above, Just replace instances of the int with safe<int>.

#include <cassert>
#include <stdexcept>
#include <iostream>

#include "../include/safe_integer.hpp"
//#include "../include/safe_compare.hpp"

void detected_msg(bool detected){
    std::cout << (detected ? "error detected!" : "error NOT detected! ") << std::endl;
}

int main(int argc, const char * argv[]){
    std::cout << "example 2:";
    std::cout << "undetected overflow in data type" << std::endl;
    try{
        int x = INT_MAX;
        // the following silently produces an incorrect result
        ++x;
        std::cout << x << " != " << INT_MAX << " + 1" << std::endl;
        detected_msg(false);
    }
    catch(...){
        assert(false); // never arrive here
    }
    // solution: replace int with safe<int>
    try{
        using namespace boost::numeric;
        safe<int> x = INT_MAX;
        // throws exception when result is past maximum possible 
        ++x;
        assert(false); // never arrive here
    }
    catch(std::range_error & e){
        std::cout << e.what();
        detected_msg(true);
    }
    return 0;
}

PrevUpHomeNext