...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Safe Numerics |
Subtracting two unsigned values of the same size will result in an
unsigned value. If the first operand is less than the second the result
will be arithmetically in correct. But if the size of the unsigned types
is less than that of an unsigned int
, C/C++ will promote the
types to signed int
before subtracting resulting in an
correct result. In either case, there is no indication of an error.
Somehow, the programmer is expected to avoid this behavior. Advice usually
takes the form of "Don't use unsigned integers for arithmetic". This is
well and good, but often not practical. C/C++ itself uses unsigned for
sizeof(T)
which is then used by users in arithmetic.
This program demonstrates this problem. The solution is to replace instances of built in integer types with corresponding safe types.
// 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> int main(int, const char *[]){ std::cout << "example 8:"; std::cout << "undetected erroneous expression evaluation" << std::endl; std::cout << "Not using safe numerics" << std::endl; try{ unsigned int x = 127; unsigned int y = 2; unsigned int z; // this produces an invalid result ! z = y - x; std::cout << "error NOT detected!" << std::endl; std::cout << z << " != " << y << " - " << x << std::endl; } catch(const std::exception &){ std::cout << "error detected!" << std::endl; } // solution: replace int with safe<int> std::cout << "Using safe numerics" << std::endl; try{ using namespace boost::safe_numerics; safe<unsigned int> x = 127; safe<unsigned int> y = 2; safe<unsigned int> z; // rather than producing an invalid result an exception is thrown z = y - x; std::cout << "error NOT detected!" << std::endl; std::cout << z << " != " << y << " - " << x << std::endl; } catch(const std::exception & e){ // which we can catch here std::cout << "error detected:" << e.what() << std::endl; } return 0; }
example 8:undetected erroneous expression evaluation Not using safe numerics error NOT detected! 4294967171 != 2 - 127 Using safe numerics error detected:subtraction result cannot be negative: negative overflow error Program ended with exit code: 0