Help Needed with Chapter 4 Drill in PPP2

Please help me with this code (just read the comments to know what the drill is asking for since I've mentioned all of the specs there (the problem I'm having with the code is that 1. last I checked, not all of the values entered were going into the vector which caused it to have less values than it should've had; 2. a range-error exception is being triggered somewhere in my code because of std_lib_facilities.h's vector::operator[] code, but I don't know where it's being triggered and I'm having trouble using the Windows Debugger in VS)):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Osman Zakir
// 12 / 19 / 2016
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 4 Drill

// Drill specs:
/**
 * 1. Write a program that consists of a while -loop that (each time around the
 *	loop) reads in two int s and then prints them. Exit the program when a
 *	terminating '|' is entered.
 * 2. Change the program to write out "the smaller value is:" followed by the
 *	smaller of the numbers and "the larger value is:" followed by the larger
 *	value.
 * 3. Augment the program so that it writes the line "the numbers are equal"
 *	(only) if they are equal.
 * 4. Change the program so that it uses doubles instead of ints
 * 5. Change the program so that it writes out "the numbers are almost equal"
 *	after writing out which is the larger and the smaller if the two numbers
 *	differ by less than 1.0/100.
 * 6. Now change the body of the loop so that it reads just one double each
 *	time around. Define two variables to keep track of which is the smallest
 *	and which is the largest value you have seen so far. Each time through
 *	the loop write out the value entered. If it’s the smallest so far, write "the
 *	smallest so far" after the number. If it is the largest so far, write "the largest
 *	so far after the number".
 * 7. Add a unit to each double entered; that is, enter values such as 10cm ,
 *	2.5in , 5ft , or 3.33m . Accept the four units: cm , m , in , ft . Assume con-
 *	version factors 1m == 100cm , 1in == 2.54cm , 1ft == 12in . Read the unit
 *	indicator into a string. You may consider 12 m (with a space between the
 *	number and the unit) equivalent to 12m (without a space).
 * 8. Reject values without units or with “illegal” representations of units, such
 *	as y , yard , meter , km , and gallons.
 * 9. Keep track of the sum of values entered (as well as the smallest and the
 *	largest) and the number of values entered. When the loop ends, print the
 *	smallest, the largest, the number of values, and the sum of values. Note
 *	that to keep the sum, you have to decide on a unit to use for that sum; use
 *	meters.
 */

#include "../../std_lib_facilities.h"

int main()
{
	double value = 0;
	double smaller = 32767.0;
	double larger = -32767.0;
	double meters = 0;
	double cm_to_m = 100.0, in_to_m = 0.0254, ft_to_m = 0.3048;
	double sum = 0;
	string unit = "";
	vector<double> numbers_entered;
	std::cout << "Enter values with units; enter '|' to stop\n"
			  << "(accepted units are: cm, m, in, ft\n"
			  << "all values will be converted to meters):\n";
	while (std::cin >> value >> unit)
	{
		numbers_entered.push_back(value);

		if (unit == "cm")
		{
			meters = numbers_entered[value] / cm_to_m;
		}
		else if (unit == "ft")
		{
			meters = numbers_entered[value] * ft_to_m;
		}
		else if (unit == "in")
		{
			meters = numbers_entered[value] * in_to_m;
		}
		else if (unit == "m")
		{
			meters = numbers_entered[value];
		}
		else
		{
			std::cout << "Error: invalid unit. Please try again later.\n";
		}
		
		if (numbers_entered[value] > larger)
		{
			larger = numbers_entered[value];
		}
		if (numbers_entered[value] < smaller)
		{
			smaller = numbers_entered[value];
		}
	}

	for (int i = 0; static_cast<size_t>(i) < numbers_entered.size(); ++i)
	{
		sum += numbers_entered[i];
	}

	std::cout << fixed;
	std::cout << setprecision(2);
	std::cout << "The sum of all values entered is: " << sum << "\n"
			  << "The number of values entered is: "  << numbers_entered.size() << "\n"
			  << "The smallest value entered is: "    << smaller << "\n"
			  << "The largest value entered is: "     << larger << "\n\n";
	std::sort(numbers_entered.begin(), numbers_entered.end());
	std::cout << "All of the numbers entered are:\n";
	for (auto const &x : numbers_entered)
	{
		std::cout << x << "\n";
	}
	std::cin.clear();
	keep_window_open();
}
If you want to use the value you should use value in the code instead of numbers_entered[value].
That's probably it, yeah. But then I want to get at the numbers inside the vector and convert them all to meters before calculating anything else (like the largest value, smallest value, etc..).
Easiest is probably to do the conversion before you add the value to the vector.

If you want to access an element in the vector you need to use an index. You can also use the back() and front() functions to access the first and last values (e.g. numbers_entered.back()).
You may consider 12 m (with a space between the number and the unit) equivalent to 12m (without a space)

... you'd need getline() to cover both
Exit the program when a terminating '|' is entered
the program, as it stands, terminates on | as well as some of the other characters like ?, / etc
And yes, you do have to convert all input to meters before entering them. There are other input validation points to consider as well such as decimals, leading non-digits, recurring decimals (something that I've mentioned but not validated explicitly) and probably several others I haven't thought of. However putting the current bunch together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <sstream>
#include <numeric>
using std::cin; using std::cout;

const std::vector<std::string> units {"cm", "ft", "in", "m"};
constexpr double cm_to_m = 100.0, in_to_m = 0.0254, ft_to_m = 0.3048;

template <typename T>
struct Input
{
    T _value;
    std::string _unit;
    Input() : _value(0), _unit(""){}
};
int main()
{
	double value {}, smaller {}, larger{}, meters {}, sum{8};
    std::string unit = "";
	std::vector<double> numbers_entered;
	bool fQuit = false;

	while(!fQuit)
    {
        cout << "Enter values with units; enter '|' to stop\n"
            << "(accepted units are: cm, m, in, ft\n"
            << "all values will be converted to meters):\n";
        std::string line;
        bool num_read = false;
        getline(cin, line);
        if (line == "|")
        {
            fQuit = true;
            break;
        }
        else
        {
            Input<double> in;
            std::string value{};
            if(!isdigit(line[0]))
            {
                cout << "Invalid entry, try again \n";
            }
            else
            {
                for (auto& elem: line)
                {
                    if(isdigit(elem)||elem == '.')
                    {
                        value = value + elem;//can have further checks for no more than one decimal
                    }
                    else if (!isspace(elem))
                    {
                        in._unit  = in._unit + elem;
                    }
                }
                std::stringstream stream(value);
                stream >> in._value;
                if((in._unit == "")||(std::find(units.begin(), units.end(), in._unit) == units.end()))
                {
                    cout << "Invalid unit, try again \n";
                }
                else if (in._unit == "cm")
                {
                    meters = in._value / cm_to_m;
                    num_read = true;
                }
                else if (in._unit == "ft")
                {
                    meters = in._value * ft_to_m;
                    num_read = true;
                }
                else if (in._unit == "in")
                {
                    meters = in._value * in_to_m;
                    num_read = true;
                }
                else if (in._unit == "m")
                {
                    meters = in._value;
                    num_read = true;
                }
                if(num_read == true)
                {
                    numbers_entered.push_back(meters);
                    if (numbers_entered.back() > larger)
                    {
                        larger = numbers_entered.back();
                        cout << "Largest so far \n";
                    }
                    smaller = *std::min_element (numbers_entered.begin(), numbers_entered.end());
                    if (smaller == numbers_entered.back())
                    {
                        cout << "Smallest so far \n";
                    }
                }
            }
        }
    }
    sum = std::accumulate (numbers_entered.begin(), numbers_entered.end(), 0.0);

	cout << std::fixed;
	cout << std::setprecision(6);
	cout << "The sum of all values entered is: " << double(sum) << "\n"
			  << "The number of values entered is: "  << numbers_entered.size() << "\n"
			  << "The smallest value entered is: "    << smaller << "\n"
			  << "The largest value entered is: "     << larger << "\n\n";
	cout << "All of the numbers entered are:\n";
	for (auto const &elem : numbers_entered)
	{
		cout << elem << "\n";
	}
}
Last edited on
@gunnerfunner: I tested your above code and it seemed fine for rejecting bad input, but unfortunately there are still some bugs I found.

Either way, I made some more changes.

I think it's fine like this (though I do need a way to stop only on '|' and I have to make sure it reacts sensibly if something other than a number is entered for "value" - I think @gunnerfunner's code for that is good, but it's a bit too complicated), but if anyone has suggestions for efficiency, I'm for them (well, I at least want to know of a good way to split this up into separate functions, like a separate function for the input loop, a separate function for each of the calculations, etc..):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Osman Zakir
// 12 / 19 / 2016
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 4 Drill

// Drill specs:
/**
 * 1. Write a program that consists of a while -loop that (each time around the
 *	loop) reads in two int s and then prints them. Exit the program when a
 *	terminating '|' is entered.
 * 2. Change the program to write out "the smaller value is:" followed by the
 *	smaller of the numbers and "the larger value is:" followed by the larger
 *	value.
 * 3. Augment the program so that it writes the line "the numbers are equal"
 *	(only) if they are equal.
 * 4. Change the program so that it uses doubles instead of ints
 * 5. Change the program so that it writes out "the numbers are almost equal"
 *	after writing out which is the larger and the smaller if the two numbers
 *	differ by less than 1.0/100.
 * 6. Now change the body of the loop so that it reads just one double each
 *	time around. Define two variables to keep track of which is the smallest
 *	and which is the largest value you have seen so far. Each time through
 *	the loop write out the value entered. If it’s the smallest so far, write "the
 *	smallest so far" after the number. If it is the largest so far, write "the largest
 *	so far after the number".
 * 7. Add a unit to each double entered; that is, enter values such as 10cm ,
 *	2.5in , 5ft , or 3.33m . Accept the four units: cm , m , in , ft . Assume con-
 *	version factors 1m == 100cm , 1in == 2.54cm , 1ft == 12in . Read the unit
 *	indicator into a string. You may consider 12 m (with a space between the
 *	number and the unit) equivalent to 12m (without a space).
 * 8. Reject values without units or with “illegal” representations of units, such
 *	as y , yard , meter , km , and gallons.
 * 9. Keep track of the sum of values entered (as well as the smallest and the
 *	largest) and the number of values entered. When the loop ends, print the
 *	smallest, the largest, the number of values, and the sum of values. Note
 *	that to keep the sum, you have to decide on a unit to use for that sum; use
 *	meters.
 */

#include "../../std_lib_facilities.h"

int main()
{
	double value = 0;
	double smaller = std::numeric_limits<double>::max();
	double larger = std::numeric_limits<double>::min();
	double meters = 0;
	double cm_to_m = 100.0, in_to_m = 0.0254, ft_to_m = 0.3048;
	double sum = 0;
	string unit = "";
	vector<double> numbers_entered;
	cout << "Enter values with units; enter '|' to stop\n"
			  << "(accepted units are: cm, m, in, ft\n"
			  << "all values will be converted to meters):\n";
	while (cin >> value >> unit)
	{
		if (unit == "cm")
		{
			meters = value / cm_to_m;
			numbers_entered.push_back(meters);
		}
		else if (unit == "ft")
		{
			meters = value * ft_to_m;
			numbers_entered.push_back(meters);
		}
		else if (unit == "in")
		{
			meters = value * in_to_m;
			numbers_entered.push_back(meters);
		}
		else if (unit == "m")
		{
			meters = value;
			numbers_entered.push_back(meters);
		}
		else
		{
			cout << "Error: invalid unit. Please try again later.\n";
		}
	}

	for (int i = 0; static_cast<size_t>(i) < numbers_entered.size(); ++i)
	{
		sum += numbers_entered[i];
		if (numbers_entered[i] > larger)
		{
			larger = numbers_entered[i];
		}
		if (numbers_entered[i] < smaller)
		{
			smaller = numbers_entered[i];
		}
	}
	sort(numbers_entered);
	cout << fixed;
	cout << setprecision(2);
	cout << "The sum of all values entered is: " << sum << "\n"
		 << "The number of values entered is: "  << numbers_entered.size() << "\n"
		 << "The smallest value entered is: "    << smaller << "\n"
		 << "The largest value entered is: "     << larger << "\n\n";
	cout << "All of the numbers entered are:\n";
	for (auto const &x : numbers_entered)
	{
		cout << x << "\n";
	}
	cin.clear();
	cin.ignore();
	keep_window_open();}


Last edited on
Why the following cast?
for (int i = 0; static_cast<size_t>(i) < numbers_entered.size(); ++i)

You should just use the correct type in the first place.

for (size_t i = 0; i < numbers_entered.size(); ++i)

Also you could do the summing operation in your read loop instead of adding another loop. You could also compute the largest and smallest in the read loop or this loop and avoid the sort operation. By the way where is your sort() defined and implemented?

EDIT: What parts of the assignment is this program implementing at this time?

2. a range-error exception is being triggered somewhere in my code because of std_lib_facilities.h's vector::operator[] code

If I remember correctly part of the "modifications" of the std::vector were to change the operator[] to do range checking by using the .at() member function. Your debugger should be able to tell you exactly where it detects the problem, as long as you're compiling in the debug information.

Last edited on
The drill says to sort the numbers before printing them out. That's why I sort them. And this version of sort() is defined in that header file Stroustrup wants us to use with the book.

And the reason why I make it an int and then cast it to a size_t is that I want the loop variable to be an int throughout the loop.

As for the summing, I think it'd be better in a loop where I have a good index variable to use. A while-loop wouldn't be good and the book says to use a while loop for taking the input.

Anyway, I think I need to put this here again (made some more edits to the code):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Osman Zakir
// 12 / 19 / 2016
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 4 Drill

// Drill specs:
/**
 * 1. Write a program that consists of a while -loop that (each time around the
 *	loop) reads in two int s and then prints them. Exit the program when a
 *	terminating '|' is entered.
 * 2. Change the program to write out "the smaller value is:" followed by the
 *	smaller of the numbers and "the larger value is:" followed by the larger
 *	value.
 * 3. Augment the program so that it writes the line "the numbers are equal"
 *	(only) if they are equal.
 * 4. Change the program so that it uses doubles instead of ints
 * 5. Change the program so that it writes out "the numbers are almost equal"
 *	after writing out which is the larger and the smaller if the two numbers
 *	differ by less than 1.0/100.
 * 6. Now change the body of the loop so that it reads just one double each
 *	time around. Define two variables to keep track of which is the smallest
 *	and which is the largest value you have seen so far. Each time through
 *	the loop write out the value entered. If it’s the smallest so far, write "the
 *	smallest so far" after the number. If it is the largest so far, write "the largest
 *	so far after the number".
 * 7. Add a unit to each double entered; that is, enter values such as 10cm ,
 *	2.5in , 5ft , or 3.33m . Accept the four units: cm , m , in , ft . Assume con-
 *	version factors 1m == 100cm , 1in == 2.54cm , 1ft == 12in . Read the unit
 *	indicator into a string. You may consider 12 m (with a space between the
 *	number and the unit) equivalent to 12m (without a space).
 * 8. Reject values without units or with “illegal” representations of units, such
 *	as y , yard , meter , km , and gallons.
 * 9. Keep track of the sum of values entered (as well as the smallest and the
 *	largest) and the number of values entered. When the loop ends, print the
 *	smallest, the largest, the number of values, and the sum of values. Note
 *	that to keep the sum, you have to decide on a unit to use for that sum; use
 *	meters.
 * 10. Keep all the values entered (converted into meters) in a vector . At the
 *	end, write out those values.
 * 11. Before writing out the values from the vector , sort them (that’ll make
 *	them come out in increasing order).
 */

#include "../../std_lib_facilities.h"

int main()
{
	double value = 0;
	double smaller = std::numeric_limits<double>::max();
	double larger = std::numeric_limits<double>::min();
	double meters = 0;
	double cm_to_m = 100.0, in_to_m = 0.0254, ft_to_m = 0.3048;
	double sum = 0;
	string unit = "";
	vector<double> numbers_entered;
	cout << "Enter values with units; enter '|' to stop\n"
			  << "(accepted units are: cm, m, in, ft\n"
			  << "all values will be converted to meters):\n";
	while (cin >> value >> unit)
	{
		if (unit == "cm")
		{
			meters = value / cm_to_m;
			numbers_entered.push_back(meters);
		}
		else if (unit == "ft")
		{
			meters = value * ft_to_m;
			numbers_entered.push_back(meters);
		}
		else if (unit == "in")
		{
			meters = value * in_to_m;
			numbers_entered.push_back(meters);
		}
		else if (unit == "m")
		{
			meters = value;
			numbers_entered.push_back(meters);
		}
		else
		{
			cout << "Error: invalid unit. Please try again later.\n";
		}
	}

	for (int i = 0; static_cast<size_t>(i) < numbers_entered.size(); ++i)
	{
		sum += numbers_entered[i];
		if (numbers_entered[i] > larger)
		{
			larger = numbers_entered[i];
		}
		if (numbers_entered[i] < smaller)
		{
			smaller = numbers_entered[i];
		}
	}
	sort(numbers_entered);
	cout << fixed;
	cout << setprecision(2);
	cout << "The sum of all values entered is: " << sum << "\n"
		 << "The number of values entered is: "  << numbers_entered.size() << "\n"
		 << "The smallest value entered is: "    << smaller << "\n"
		 << "The largest value entered is: "     << larger << "\n\n";
	cout << "All of the numbers entered are:\n";
	for (auto const &x : numbers_entered)
	{
		cout << x << "\n";
	}
	cin.clear();
	cin.ignore();
	keep_window_open();
}


I think it's fine like this (though I do need a way to stop only on '|' and I have to make sure it reacts sensibly if something other than a number is entered for "value" - I think @gunnerfunner's code for that is good, but it's a bit too complicated), but if anyone has suggestions for efficiency, I'm for them (well, I at least want to know of a good way to split this up into separate functions, like a separate function for the input loop, a separate function for each of the calculations, etc..).
Last edited on
(though I do need a way to stop only on '|'
Maybe something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const char exit_ch{ '|' };

char dummy{}; double value{}; string unit{};
while( cin.get( dummy ) ) {
    if( dummy == exit_ch ) break;

    if( isdigit( dummy ) || dummy == '.' ) {
        cin.putback( dummy );

        cin >> value >> unit;

        if( unit == "cm" ) {
            // ...
        }
        // ...
    }
}


for (int i = 0; static_cast<size_t>(i) < numbers_entered.size(); ++i)
Instead of casting, you could also do this:
for (size_t i = 0; i < numbers_entered.size(); ++i)

sort(numbers_entered);
I don't understand why you would iterate through number_entered to find the smallest/largest then sort it. Sorting it makes iterating through numbers_entered redundant as the smallest/largest numbers are at front() and back() respectively.
The drill says to sort the numbers before printing them out. That's why I sort them. And this version of sort() is defined in that header file Stroustrup wants us to use with the book.

That is the last part of the exercise (#11), you should already have kept track of the minimum and maximum for bullet #9. You should be doing each bullet one at a time from the first, not trying to do all the assignment at one time.

And the reason why I make it an int and then cast it to a size_t is that I want the loop variable to be an int throughout the loop.

Why? It should be a size_t not an int throughout the loop. A size_t is the correct type for this loop since an int may not be large enough to hold the size of the vector.

As for the summing, I think it'd be better in a loop where I have a good index variable to use. A while-loop wouldn't be good and the book says to use a while loop for taking the input.

Why do you need an index variable to keep track of the sum, just do the sum as you read the values. The same with the larger and smaller, you can do this in the read loop as well.

By the way IMO you are not accomplishing the goals of the "assignment", since it appears that you are doing the assignment out of order.



I did do them in order. I'm trying to fix remaining errors now. When I first did the "larger" and "smaller" stuff, it was working fine. I'm having trouble keeping the input buffer clean and keep tracking of the largest and smallest values when I have to convert all numbers to meters (it was fine before that).

I'll try to keep track of the largest and smallest values within the read loop, then, but I need to know how to get an index into the vector. Do I use an iterator variable (IIRC, it was "std::vector<int>::iterator" or something like that?)?

It doesn't say to take care of invalid input on the numbers, but I want to try that as well at the end anyway. Any hints or suggestions are welcome.

I'll post the code as it is now
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Osman Zakir
// 12 / 19 / 2016
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 4 Drill

// Drill specs:
/**
 * 1. Write a program that consists of a while -loop that (each time around the
 *	loop) reads in two int s and then prints them. Exit the program when a
 *	terminating '|' is entered.
 * 2. Change the program to write out "the smaller value is:" followed by the
 *	smaller of the numbers and "the larger value is:" followed by the larger
 *	value.
 * 3. Augment the program so that it writes the line "the numbers are equal"
 *	(only) if they are equal.
 * 4. Change the program so that it uses doubles instead of ints
 * 5. Change the program so that it writes out "the numbers are almost equal"
 *	after writing out which is the larger and the smaller if the two numbers
 *	differ by less than 1.0/100.
 * 6. Now change the body of the loop so that it reads just one double each
 *	time around. Define two variables to keep track of which is the smallest
 *	and which is the largest value you have seen so far. Each time through
 *	the loop write out the value entered. If it’s the smallest so far, write "the
 *	smallest so far" after the number. If it is the largest so far, write "the largest
 *	so far after the number".
 * 7. Add a unit to each double entered; that is, enter values such as 10cm ,
 *	2.5in , 5ft , or 3.33m . Accept the four units: cm , m , in , ft . Assume con-
 *	version factors 1m == 100cm , 1in == 2.54cm , 1ft == 12in . Read the unit
 *	indicator into a string. You may consider 12 m (with a space between the
 *	number and the unit) equivalent to 12m (without a space).
 * 8. Reject values without units or with “illegal” representations of units, such
 *	as y , yard , meter , km , and gallons.
 * 9. Keep track of the sum of values entered (as well as the smallest and the
 *	largest) and the number of values entered. When the loop ends, print the
 *	smallest, the largest, the number of values, and the sum of values. Note
 *	that to keep the sum, you have to decide on a unit to use for that sum; use
 *	meters.
 * 10. Keep all the values entered (converted into meters) in a vector . At the
 *	end, write out those values.
 * 11. Before writing out the values from the vector , sort them (that’ll make
 *	them come out in increasing order).
 */

#include "../../std_lib_facilities.h"

int main()
{
	double value = 0;
	double smaller = std::numeric_limits<double>::max();
	double larger = std::numeric_limits<double>::min();
	double meters = 0;
	constexpr double cm_to_m = 100.0, in_to_m = 0.0254, ft_to_m = 0.3048;
	double sum = 0;
	string unit = "";
	vector<double> numbers_entered;
	cout << "Enter values with units; enter '|' to stop\n"
			  << "(accepted units are: cm, m, in, ft\n"
			  << "all values will be converted to meters):\n";
	while (cin >> value >> unit)
	{
		if (unit == "cm")
		{
			meters = value / cm_to_m;
			numbers_entered.push_back(meters);
			sum += meters;
			value = meters;
		}
		else if (unit == "ft")
		{
			meters = value * ft_to_m;
			numbers_entered.push_back(meters);
			sum += meters;
			value = meters;
		}
		else if (unit == "in")
		{
			meters = value * in_to_m;
			numbers_entered.push_back(meters);
			sum += meters;
			value = meters;
		}
		else if (unit == "m")
		{
			meters = value;
			numbers_entered.push_back(meters);
			sum += meters;
			value = meters;
		}
		else
		{
			cout << "Error: invalid unit. Please try again.\n";
		}

		if (value > larger)
		{
			larger = value;
			cout << value << ": the largest so far\n";
		}
		if (value < smaller)
		{
			smaller = value;
			cout << value << ": the smallest so far\n";
		}
	}
	sort(numbers_entered);
	cout << fixed;
	cout << setprecision(2);
	cout << "The sum of all values entered is: " << sum << "\n"
		 << "The number of values entered is: "  << numbers_entered.size() << "\n"
		 << "The smallest value entered is: "    << smaller << "\n"
		 << "The largest value entered is: "     << larger << "\n\n";
	cout << "All of the numbers entered are:\n";
	for (auto const &x : numbers_entered)
	{
		cout << x << "\n";
	}
	cin.clear();
	cin.ignore();
	keep_window_open();
}


Edit: Newly updated code up in there now.

There's still another bug aside from it ending whenever something other than | that would put cin in a bad state is entered. There are times when it sits idle for longer, and when I enter white-space separated numbers (on the same line), and then enter a |, the last number I typed doesn't go into the vector.
Last edited on
OP: if you type 1s or / your program collapses, you need to work more on the input validation

I tested your above code and it seemed fine for rejecting bad input, but unfortunately there are still some bugs I found
... let me know what sort of bugs you found, I am sure if there's anything it'd not be too much to fix it

@gunnerfunner's code for that is good, but it's a bit too complicated)
- don't let that put you off if it's
good
, put in some effort and whatever you learn will stay with you forever. Good luck ...
How do I write the while-loop's condition so that it'll stop when a '|' is entered? Doing it for when value is '|' didn't work right when I tried it.

@integralfx: I'll try your method for it as well since it seems good for me, but I'd still like the while loop's condition to stop the loop if a '|' is encountered. Thanks.

@gunnerfunner: Never mind on the bug, but there's a warning about an unused variable on line 22 (on 'value'). If that was intentional then never mind, but normally there shouldn't be any unused variables, right?
there shouldn't be any unused variables, right?

true, so take values out, add in the sort function before printing the vector and I think you're pretty much good to go with this one (fingers crossed!)
I'm going with @integralfx's version for how to stop input since it's easier to understand for me (no offense). But there's still a weird thing happening with invalid units; check this out (output from the most recent run of the program, with the last input I'd given it also shown):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
200y
Error: invalid unit. Please try again.
200: the largest so far
|
The sum of all values entered is: 361.85
The number of values entered is: 12
The smallest value entered is: 0.20
The largest value entered is: 200.00

All of the numbers entered are:
0.20
0.51
1.00
1.50
2.54
3.81
6.10
20.00
30.48
45.72
100.00
150.00


Here's the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Osman Zakir
// 12 / 19 / 2016
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 4 Drill

// Drill specs:
/**
 * 1. Write a program that consists of a while -loop that (each time around the
 *	loop) reads in two int s and then prints them. Exit the program when a
 *	terminating '|' is entered.
 * 2. Change the program to write out "the smaller value is:" followed by the
 *	smaller of the numbers and "the larger value is:" followed by the larger
 *	value.
 * 3. Augment the program so that it writes the line "the numbers are equal"
 *	(only) if they are equal.
 * 4. Change the program so that it uses doubles instead of ints
 * 5. Change the program so that it writes out "the numbers are almost equal"
 *	after writing out which is the larger and the smaller if the two numbers
 *	differ by less than 1.0/100.
 * 6. Now change the body of the loop so that it reads just one double each
 *	time around. Define two variables to keep track of which is the smallest
 *	and which is the largest value you have seen so far. Each time through
 *	the loop write out the value entered. If it’s the smallest so far, write "the
 *	smallest so far" after the number. If it is the largest so far, write "the largest
 *	so far after the number".
 * 7. Add a unit to each double entered; that is, enter values such as 10cm ,
 *	2.5in , 5ft , or 3.33m . Accept the four units: cm , m , in , ft . Assume con-
 *	version factors 1m == 100cm , 1in == 2.54cm , 1ft == 12in . Read the unit
 *	indicator into a string. You may consider 12 m (with a space between the
 *	number and the unit) equivalent to 12m (without a space).
 * 8. Reject values without units or with “illegal” representations of units, such
 *	as y , yard , meter , km , and gallons.
 * 9. Keep track of the sum of values entered (as well as the smallest and the
 *	largest) and the number of values entered. When the loop ends, print the
 *	smallest, the largest, the number of values, and the sum of values. Note
 *	that to keep the sum, you have to decide on a unit to use for that sum; use
 *	meters.
 * 10. Keep all the values entered (converted into meters) in a vector . At the
 *	end, write out those values.
 * 11. Before writing out the values from the vector , sort them (that’ll make
 *	them come out in increasing order).
 */

#include "../../std_lib_facilities.h"

int main()
{
	double value = 0;
	double smaller = std::numeric_limits<double>::max();
	double larger = std::numeric_limits<double>::min();
	double meters = 0;
	constexpr double cm_to_m = 100.0, in_to_m = 0.0254, ft_to_m = 0.3048;
	const char exit_ch = '|';
	double sum = 0;
	string unit = "";
	char exit_test = ' ';
	vector<double> numbers_entered;
	cout << "Enter values with units; enter '|' to stop\n"
		<< "(accepted units are: cm, m, in, ft\n"
		<< "all values will be converted to meters):\n";
	while (cin.get(exit_test))
	{
		if (exit_test == exit_ch)
		{
			break;
		}
		else if (isdigit(exit_test) || exit_test == '.')
		{
			cin.putback(exit_test);
			cin >> value >> unit;
			cin.ignore();

			if (unit == "cm")
			{
				meters = value / cm_to_m;
				numbers_entered.push_back(meters);
				sum += meters;
				value = meters;
			}
			else if (unit == "ft")
			{
				meters = value * ft_to_m;
				numbers_entered.push_back(meters);
				sum += meters;
				value = meters;
			}
			else if (unit == "in")
			{
				meters = value * in_to_m;
				numbers_entered.push_back(meters);
				sum += meters;
				value = meters;
			}
			else if (unit == "m")
			{
				meters = value;
				numbers_entered.push_back(meters);
				sum += meters;
				value = meters;
			}
			else
			{
				cout << "Error: invalid unit. Please try again.\n";
			}

			if (value > larger)
			{
				larger = value;
				cout << value << ": the largest so far\n";
			}
			if (value < smaller)
			{
				smaller = value;
				cout << value << ": the smallest so far\n";
			}
		}
	}
	sort(numbers_entered);
	cout << fixed;
	cout << setprecision(2);
	cout << "The sum of all values entered is: " << sum << "\n"
		 << "The number of values entered is: "  << numbers_entered.size() << "\n"
		 << "The smallest value entered is: "    << smaller << "\n"
		 << "The largest value entered is: "     << larger << "\n\n";
	cout << "All of the numbers entered are:\n";
	for (auto const &x : numbers_entered)
	{
		cout << x << "\n";
	}
	keep_window_open();
}


What's going on there with the 200 yards? It rejected the yards, but it still took 200 as the largest value. Should it reject the 200 as well (I didn't get that part from the drill specs, so that's why I'm asking)?

Edit: I've put the sort() call right before the ranged for-loop that's printing out all of the values from the vector, and I've put all of the "largest" and "smallest" code in the if-conditions that check for each of the valid units, so that it doesn't print "some number: the largest so far" or whatever after rejecting an invalid unit. It'll test it now. Hopefully it'll work as intended.

Edit2: It worked. And I've also decided to have the if-condition for the invalid unit test throw an exception so I can catch the exception and then print out the error message. I used std_lib_facilities.h's error() function so that a runtime_error exception is thrown. It works like a charm.
Last edited on
Topic archived. No new replies allowed.