#include #include #include #include /*************************************************** * Clamping integer values * * Sometimes we have a value that may lie outside the * range that we want to allow. * * For example, suppose an integer represents the * intensity of a pixel, where the allowed values are * * 0 through 255. * * If a calculation produces * * -17 * * we may want to replace this value with 0. Similarly, * if a calculation produces * * 312 * * we may want to replace this value with 255. * * This operation is called clamping. * * Given a value n and limits lower and upper, clamping * produces * * lower, if n < lower, * * n, if lower <= n <= upper, * * upper, if n > upper. * * For example, when clamping to the interval * * 0, ..., 255 * * we have * * -17 -> 0 * 0 -> 0 * 42 -> 42 * 255 -> 255 * 312 -> 255 * * * WHY IS CLAMPING USEFUL? * * Recall that different integer data types can * represent different ranges of values. * * On the computers used for this course, a 'short' * can represent * * -32768 through 32767, * * while an 'unsigned short' can represent * * 0 through 65535. * * Suppose an 'int' contains the value 40000. This * value cannot be represented by a 'short'. * * Before converting such a value to a smaller integer * type, we may sometimes choose to clamp it to the * range of the destination type. * * Thus, if we clamp 40000 to the range of 'short', * we obtain * * 32767. * * Similarly, * * -50000 * * would be clamped to * * -32768. * * Clamping is not the same operation as converting * one integer type to another. Rather, clamping is * something we may deliberately perform before a * conversion when we want values outside the allowed * range to be replaced by the nearest boundary. * * * THE PROBLEM WITH OUR FUNCTIONS * * Below are four clamping functions: * * clamp_int(...) * clamp_uint(...) * clamp_short(...) * clamp_ushort(...) * * Each works with a different integer data type. * * Look carefully at their function bodies. Apart * from the types of the parameters and return values, * the algorithms are identical. * * In fact, if we wanted similar functions for * * signed char * unsigned char * long * unsigned long * long long * unsigned long long * * we would have to copy essentially the same function * again and again. * * Copying code in this way is undesirable. If we * discover a bug or decide to change the algorithm, * we would have to make the same change in every copy. * * Shortly, we will see how C++ allows us to describe * this algorithm once and then use it with many * different data types. ***************************************************/ // Function declarations int main(); int clamp_int( int n, int lower, int upper ); unsigned int clamp_uint( unsigned int n, unsigned int lower, unsigned int upper ); short clamp_short( short n, short lower, short upper ); unsigned short clamp_ushort( unsigned short n, unsigned short lower, unsigned short upper ); // Function definitions int main() { std::cout << "Clamping to 0, ..., 255:" << std::endl; for ( unsigned int k{ 0 }; k < 5; ++k ) { // Generate an integer between -500 and 500, // inclusive. int n{ std::rand()%1001 - 500 }; std::cout << n << " clamped to the range " << "of an unsigned char: " << clamp_int( n, 0, std::numeric_limits::max() ) << std::endl; } std::cout << std::endl; std::cout << "Clamping to the range of 'short':" << std::endl; for ( unsigned int k{ 0 }; k < 5; ++k ) { // Generate an integer between -50000 and 50000, // inclusive. int n{ std::rand()%100001 - 50000 }; std::cout << n << " clamped to 'short': " << clamp_int( n, std::numeric_limits::min(), std::numeric_limits::max() ) << std::endl; } std::cout << std::endl; std::cout << "Clamping to the range of 'unsigned short':" << std::endl; for ( unsigned int k{ 0 }; k < 5; ++k ) { // Again, generate an integer between -50000 // and 50000, inclusive. int n{ std::rand()%100001 - 50000 }; std::cout << n << " clamped to 'unsigned short': " << clamp_int( n, 0, std::numeric_limits::max() ) << std::endl; } return 0; } // int clamp_int( ... ) // // Return n if it lies between lower and upper. // Otherwise, return the nearest boundary. // // The assertion verifies the precondition that the // lower boundary is not greater than the upper // boundary. int clamp_int( int n, int lower, int upper ) { assert( lower <= upper ); if ( n <= lower ) { return lower; } else if ( n >= upper ) { return upper; } else { return n; } } // unsigned int clamp_uint( ... ) // // Notice that the algorithm below is identical to // that in clamp_int(...). Only the data type differs. unsigned int clamp_uint( unsigned int n, unsigned int lower, unsigned int upper ) { assert( lower <= upper ); if ( n <= lower ) { return lower; } else if ( n >= upper ) { return upper; } else { return n; } } // short clamp_short( ... ) // // Again, the algorithm is identical. short clamp_short( short n, short lower, short upper ) { assert( lower <= upper ); if ( n <= lower ) { return lower; } else if ( n >= upper ) { return upper; } else { return n; } } // unsigned short clamp_ushort( ... ) // // And once again, only the data type has changed. unsigned short clamp_ushort( unsigned short n, unsigned short lower, unsigned short upper ) { assert( lower <= upper ); if ( n <= lower ) { return lower; } else if ( n >= upper ) { return upper; } else { return n; } }