Exercise 4-3
What happens if we rewrite the previous program to allow values up to but not including 1000 but neglect to change the arguments to setw? Rewrite the program to be more robust in the face of changes that allow i to grow without adjusting the setw arguments.
Solution
Though there are many ways to solve this problem, below is the solution strategy that I use, with the aim of enhanced flexibility:
- Obtain the asymmetric range [m,n) from the user with the condition of n > m. In other words, m is the startNumber. n – 1 is the endNumber.
- Compute the number of elements within this asymmetric range as n – m. This is equivalent to the number of loops required (or number of rows to output).
- Create a function getStreamWidth that will be used to automatically compute the maximum width required for column 1 (the list of numbers), and column 2 (the square of column 1). This function is capable of dealing with both negative and non-negative input values.
- Have a for loop to output column 1 and 2 using the corresponding column stream widths computed upfront (step 3 above).
The Project
I have decided to partition the program as follows – for practice sake.
C++ Source Files
- main.cpp – this is the first program that is run during the implementation phase.
- getStreamWidth.cpp – contains all functions relating to obtaining the stream widths.
C++ Header Files
- getStreamWidth.h– declare the functions as defined in getStreamWidth.cpp.
This diagram below shows what the Code::Block Management Tree look like after successful creation of these files.
The actual content of the source and header files are documented in the following sections.
Source Files
main.cpp
#include <iostream> #include <ios> #include <iomanip> #include <algorithm> #include "getStreamWidth.h" using std::cout; // <iostream> using std::cin; // <iostream> using std::endl; // <iostream> using std::streamsize; // <ios> using std::setw; // <iomanip> using std::max; // <algorithm> int main() { #include <iostream> #include <ios> #include <iomanip> #include <algorithm> #include "getStreamWidth.h" using std::cout; // <iostream> using std::cin; // <iostream> using std::endl; // <iostream> using std::streamsize; // <ios> using std::setw; // <iomanip> using std::max; // <algorithm> int main() { // display program intro message cout << "***********************************************************\n" << "*** This program computes the square of the numbers ***\n" << "*** in the asymmetric range [m,n). ***\n" << "*** (Limitation: please ensure n > m) ***\n" << "*** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) ***\n" << "***********************************************************"; cout << endl; // ask user to supply m cout << "Enter m: "; int m; cin >> m; // ask user to supply n cout << "Enter n: "; int n; cin >> n; // ensure m and n are input correctly. If not, exit program. if (n <= m) { cout << "Please make sure n > m"; return 1; } // initialise value const int startNumber = m; // first output integer const int endNumber = n - 1; // last output integer const int numLoops = n - m; // number of rows to output // find the maxwidth for column 1 and 2 const streamsize col1Width = max(getStreamWidth(startNumber), getStreamWidth(endNumber)); const streamsize col2Width = max(getStreamWidth(startNumber * startNumber), getStreamWidth(endNumber * endNumber)); // display a summary cout << "Asymmetric range: [" << m << "," << n << ")" << endl; cout << "Number of rows = " << numLoops << endl; cout << "Column 1 width = " << col1Width << " | Column 2 width = " << col2Width << endl; // get ready to print report int y = startNumber; for (int i = 0; i != numLoops; ++i) { cout << setw(col1Width) << y << setw(col2Width) << (y * y) << setw(0) << endl; ++y; } return 0; }
getStreamWidth.cpp
#include <ios> using std::streamsize; // return the required streamsize to fit a particular integer number streamsize getStreamWidth(int number) { streamsize numDigits; // initialise numDigits and number depending on whether value is positive / negative. // If negative, require at least 2 spaces to fit the leading empty space string and the negative sign // If positive, require at least 1 space to fit the leading empty space string if (number < 0) { numDigits = 2; number *= -1; } else numDigits = 1; // numDigits is the number of divisions required to make number approaches zero (plus leading space and sign) // i.e. this is equivalent to the total stream width required while (number != 0) { ++numDigits; number /= 10; } return numDigits; }
Header Files
getStreamWidth.h
#ifndef GUARD_GETSTREAMWIDTH_H #define GUARD_GETSTREAMWIDTH_H std::streamsize getStreamWidth(int number); #endif // GUARD_GETSTREAMWIDTH_H
Test Results
Asymmetric range [-3, 4)
*********************************************************** *** This program computes the square of the numbers *** *** in the asymmetric range [m,n). *** *** (Limitation: please ensure n > m) *** *** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) *** *********************************************************** Enter m: -3 Enter n: 4 Asymmetric range: [-3,4) Number of rows = 7 Column 1 width = 3 | Column 2 width = 2 -3 9 -2 4 -1 1 0 0 1 1 2 4 3 9
Asymmetric range [995,1006)
*********************************************************** *** This program computes the square of the numbers *** *** in the asymmetric range [m,n). *** *** (Limitation: please ensure n > m) *** *** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) *** *********************************************************** Enter m: 995 Enter n: 1006 Asymmetric range: [995,1006) Number of rows = 11 Column 1 width = 5 | Column 2 width = 8 995 990025 996 992016 997 994009 998 996004 999 998001 1000 1000000 1001 1002001 1002 1004004 1003 1006009 1004 1008016 1005 1010025
Asymmetric range [-1005,-996)
*********************************************************** *** This program computes the square of the numbers *** *** in the asymmetric range [m,n). *** *** (Limitation: please ensure n > m) *** *** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) *** *********************************************************** Enter m: -1005 Enter n: -996 Asymmetric range: [-1005,-996) Number of rows = 9 Column 1 width = 6 | Column 2 width = 8 -1005 1010025 -1004 1008016 -1003 1006009 -1002 1004004 -1001 1002001 -1000 1000000 -999 998001 -998 996004 -997 994009
Asymmetric range [0,1000)
*********************************************************** *** This program computes the square of the numbers *** *** in the asymmetric range [m,n). *** *** (Limitation: please ensure n > m) *** *** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) *** *********************************************************** Enter m: 0 Enter n: 1000 Asymmetric range: [0,1000 Number of rows = 1000 Column 1 width = 4 Column 2 width = 7 0 0 1 1 2 4 ... 993 986049 994 988036 995 990025 996 992016 997 994009 998 996004 999 998001
on line 63 & 64 on main.cpp, why did you use streamsize instead of just assigning the value of the function to an int? Like so:
int x = max(getStreamWidth(startNumber)……)
wouldnt this still work?
I used
streamsize
instead ofint
because the functiongetStreamWidth()
returns astreamsize
type output. So assigning astreamsize
value to astreamsize x
seem to make sense to me. Moreover it avoids the need for C++ to implicitly convert astreamsize
type value into aint
type value. Nevertheless, it turns out that bothint
andstreamsize
types are both signed. see this article. So in this case, my guess is,int x
might work as well asstreamsize x
. Try and see!why do you have two mains in main.cpp?
The solution here is unnecessarily complicated considering the assignment, and it is way beyond the student level. The ‘enhanced flexibility’ only means that the example is incomprehensible for a student at this level. Besides taking into account negatives when we are dealing with squares (always positive )is another pointless complication. just make sure the 1st column is wide enough to have room for the minus sign.
This is really all it takes, and it can be done with what the student is supposed to know thus far: (those mathematically inclined can replace the loop in columnwidth with ln(max)…)
columnwidth(int n){
int w=0;
while (n>0){
n /= 0; // same as n=n/10
++w;
}
return w+2;
}
[…]
int max=1000;
for (int i=0; i<max+1;++i)
cout << setw(columwidth(max)) << i
<< setw(columnwidth(maxmax) << ii << endl;
line 4 is supposed to be n /= 10
A way to not have to write the getStreamWidth function is to use std::to_string instead:
int streamWidth = std::to_string(number).size();
Although it requires -std=c++0x option to compile with gcc