0% found this document useful (0 votes)
14 views7 pages

Debugging User Input Functions

The document outlines an individual homework assignment focused on debugging seven specific functions related to input validation and integer overflow in C++. Each function has defined requirements and examples, and students are instructed to work independently while adhering to the Aggie Code of Honor. Additionally, it provides guidance on debugging practices and understanding integer overflow in C++.

Uploaded by

karti
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views7 pages

Debugging User Input Functions

The document outlines an individual homework assignment focused on debugging seven specific functions related to input validation and integer overflow in C++. Each function has defined requirements and examples, and students are instructed to work independently while adhering to the Aggie Code of Honor. Additionally, it provides guidance on debugging practices and understanding integer overflow in C++.

Uploaded by

karti
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

HW: Debugging, Input Validation, and

Integer Overflow
This is an individual assignment. Do not share your code with other students. Do not show
your code to other students. Do not look at the code of other students. Do not ask other
students how they solved a problem. HWs are individual practice assignments (LWs, on the
other hand, are collaborative practice assignments). If you have questions about this
assignment, talk to a TA or an instructor.

If you know about a student who is sharing their code with other students (or in any other way is
violating the Aggie Code of Honor), you should report them to the instructor or to the AHSO
(make an honor code violation report).

tl;dr (too long; didn't read)


Debug seven “simple” functions. Buggy code and tests are on Mimir.

Requirements
Debug the following seven functions1:
1. Largest which takes three integer inputs: a, b and c. The function should return the
largest of these three values.
a. Example: Largest(1, 2, 3) should return 3.
b. Largest should not throw an exception.
c. The function prototype is int Largest(int a, int b, int c).
2. SumIsEven which takes two integer inputs and returns true if and only if the sum of
the two values is an even number. If the sum of the input values is an odd number, then
the function must return false.
a. Example: SumIsEven(3, 5) should return true.
b. SumIsEven should not throw an exception.
c. The function prototype is bool SumIsEven(int a, int b).
3. BoxesNeeded which takes one input. The input value represents a number of apples
that need to be packed into boxes. Each box can store a maximum of 20 apples. The
function should return the minimum number of boxes needed to store the given number
of apples.
a. Examples:
i. BoxesNeeded(13) should return 1.

1
Problems sourced and adapted from Ettles, Andrew & Luxton-Reilly, Andrew & Denny, Paul. (2018).
Common logic errors made by novice programmers. 83-89. 10.1145/3160489.3160493.
ii. BoxesNeeded(-13) should return 0.
b. BoxesNeeded should not throw an exception.
c. The function prototype is int BoxesNeeded(int apples).
4. SmarterSection which takes four inputs and determines which section performed
better on a particular exam question. The first input is the number of students in section
A that answer the question correctly, and the second input is the total number of
students in section A. Similarly, the third input is the number of students in section B that
answer the question correctly, and the fourth input is the total number of students in
section B. The performance of each section can be measured as the percentage of
students who answer the question correctly. The function should return true if section
A performs strictly better than section B and false otherwise.
a. Example: SmarterSection(40, 50, 75, 100) should return true.
b. SmarterSection should throw std::invalid_argument if the arguments
are invalid.
c. The function prototype is bool SmarterSection(int A_correct, int
A_total, int B_correct, int B_total).
5. GoodDinner which takes two inputs: the number of pizzas you have eaten, and a
Boolean which is true if it is the weekend and false otherwise. The function should
return true if you had a good dinner, and false if you didn’t. A good dinner is a
dinner where you eat between 10 and 20 pizzas (inclusive) unless it is a weekend, in
which case there is no upper bound on the number of pizzas.
a. Example: GoodDinner(13, false) should return true.
b. GoodDinner should not throw an exception.
c. The function prototype is bool GoodDinner(int pizzas, bool
is_weekend).
6. SumBetween which takes two integer inputs, low and high. This function should
calculate the sum of all integers between low and high, inclusive.
a. Example: SumBetween(1,10) should return 55.
b. SumBetween should throw std::invalid_argument if low and high are
out of order.
c. SumBetween should throw std::overflow_error if the sum exceeds the
maximum/minimum value of int.
i. INT32_MAX is the maximum value of a 32-bit integer.
ii. INT32_MIN is the minimum value of a 32-bit integer.
iii. See Integer Overflow for more explanation.
d. The function prototype is int SumBetween(int low, int high).
e. Try to only use int. Don't use a bigger type (it can work, but is not in the spirit of
the problem).

HINT 1: Suppose a and b are two positive integers such that (a + b) results in an
overflow. To form conditions for overflow_error, consider using subtraction
expressions (INT32_MAX - a or INT32_MAX - b) instead of addition. Likewise,
think about forming conditions with INT32_MIN.
HINT 2: Consider how you could modify low and high so that you can avoid
overflow during calculating the sum. For example, if low = INT32_MIN (-
2147483648) and high = INT32_MAX (2147483647), there is no need to
sum at all (the “sum” is simply -2147483648).
7. Product which takes two integer inputs: a and b. The function should return the product
of these two values.
a. Example: Product(2, 2) should return 4.
b. Product should throw std::overflow_error if the product exceeds the
maximum/minimum value of int.
i. INT32_MAX is the maximum value of a 32-bit integer.
ii. INT32_MIN is the minimum value of a 32-bit integer.
iii. See Integer Overflow for more explanation.
c. The function prototype is int Product(int a, int b).
d. Try to only use int. Don't use a bigger type (it can work, but is not in the spirit of
the problem).

HINT: Consider forming conditions for std::overflow_error by using a, b, and


INT32_MAX using division expressions instead of multiplication. Likewise, use a,
b, and INT32_MIN in division expressions (for negative product values).

General Instructions
Each of the seven functions already has a buggy implementation provided on Mimir.

1. Get Starter Code


A convenient way to compile multiple source files (in this case the two files:
[Link] and [Link]) is to put all the source and header files in a
directory (for example, hw_debugging) and run g++ on all source files in this directory:
● Download the starter code from Mimir.
● Make a directory named hw_debugging.
● Put the starter code in hw_debugging.
2. Remove Syntax Errors
In a terminal (assuming you are in the parent directory of hw_debugging):

$ cd hw_dubugging
$ ls
[Link] [Link] functions.h
$ g++ -std=c++17 -Wall -Wextra -Weffc++ -pedantic -g *.cpp

The * symbol is a wildcard that matches any valid character in an identifier. So, *.cpp
means all files in the current directory whose name ends in .cpp (i.e. all C++ source
files).
● Correct the syntax error(s), if any. Don’t worry about warnings for now since
warnings don’t prevent successful compilation of the code. Most warnings should
be resolved as you debug each function
○ Note that a syntax error can get the compiler off track. So, it might end up
identifying things as errors that are not really errors. So sometimes when
you fix the first syntax error and recompile, many if not all of the
subsequent errors are resolved!
○ Fix the first compiler error and then re-compile.
i. Repeat until there are no syntax errors.
3. Debug Functions
Do not completely rewrite the functions. Try to make the fewest and smallest changes
possible to fix the errors. Only if it seems that the implementation is doomed to fail
should you consider trashing and rewriting the code.
● The problems generally get a little bit harder as you go from problem 1 to
problem 7. You don’t have to go in order, but you probably should.
● Go through each function and debug one function at a time using the
Requirements listed above.
○ You will have to think carefully about what the requirements are and
come up with test cases that you can use to debug.
○ We have provided a main function (in [Link]) that you can use
to input test values for each function. You can use the provided main. You
are also free to edit and/or replace it with your own custom test code to
call the functions. None of the test cases rely on the provided main
function.
○ Do not try to read from standard input in the functions. Nothing will come.
Your program will starve and timeout. That will be sad. The input comes
from the function parameters. You can specify the input by writing test
cases in the main function or using the existing main function and
providing values by hand when you run it locally.
● When you feel you have a function working, submit your code to Mimir and see if
you pass the test cases for that function.
○ Click the tests for more details. Every test will give you useful information.
● If you don’t understand an error or get stuck, try searching for the error message
first, then contact your TA or instructor. Do not spend more than about 20
minutes stuck before asking for help.
○ Stuck means making no progress. You may have an idea of what to do
and need to read some documentation to figure out how to do it, which
might take several hours. That’s OK. You’re not stuck… yet.
i. For example, you’ll need to know how to detect arithmetic
overflows and throw exceptions. Learning this will take some
time. That time spent learning is not time spent being stuck.
However, if, once you are attempting to implement a fix, you
spend more than about 20 minutes attempting to debug the code
you wrote to fix the original error, you now are stuck. Get help.

Have Fun!

Appendix: Integer Overflow


Integers in C++ come in various sizes:
8 bits, e.g. char (int8_t)
16 bits, e.g. short (int16_t)
32 bits, e.g. int (int32_t)
64 bits, e.g. long (int64_t)

Each of these types of integers can store only a certain range of numbers, based on their size
and signed-ness (totally a word… I used it, you knew what I meant, it's a word.).
8-bit integers store integers in the range [-128, 127] or [0, 255] (unsigned)
16-bit integers store integers in the range [-32768, 32767] or [0, 65535] (unsigned)
32-bit integers store integers in the range [-2147483648, 2147483,647] or [0, 4294967295]
(unsigned).
64-bit integers store integers in the range [-9223372036854775808, 9223372036854775807] or
[0, 18446744073709551615] (unsigned).

For convenience, C++ has names for these constant limits; see them all at
[Link]. For example:
#define INT8_MIN -128
#define INT8_MAX 127

Signed integers have one bit reserved to encode the sign of the number, while the rest of the
bits are used to encode the magnitude. Unsigned integers use all the bits for the magnitude.
This is why unsigned numbers can represent “bigger” numbers: they sacrifice negative numbers
in exchange for bigger positive numbers.

This is an example of a 8-bit signed integer (i.e. char, this one is 65, or ‘A’,):
0 1 0 0 0 0 0 1
^ ^-----------^
| magnitude bits
sign bit

If we add 1 to 65 (‘A’), we get 66 (‘B’):

0 1 0 0 0 0 0 1
+ 1
-----------------
0 1 0 0 0 0 1 0

If we keep adding 1, we eventually reach 126 (‘~’) and then 127 (DEL, not printable).

0 1 1 1 1 1 1 0 // 126
+ 1
-----------------
0 1 1 1 1 1 1 1 // 127

If we add 1 to 127, we get 128, right?

0 1 1 1 1 1 1 1 // 127
+ 1
-----------------
1 0 0 0 0 0 0 0 // 128?

Actually… no. Since our integer is signed, we get -128.

1 0 0 0 0 0 0 0 // -128
^ ^-----------^
| magnitude
sign
That looks like -0, right? It’s not. Signed integers are represented using something called 2’s
complement. Don’t worry about how it works right now (it’s actually simple… but also out of
scope for this class).

The important thing to note is that the magnitude of the number overflowed into the sign bit. A
similar thing happens to unsigned numbers that get too big, except the overflow just falls off the
end and is lost:

1 1 1 1 1 1 1 1 // unsigned 255
+ 1
-----------------
1|0 0 0 0 0 0 0 0 // unsigned 0
^
overflow is lost

This problem, where a mathematical operation on two integers results in a number which does
not fit into the data type (e.g. two 8-bit numbers added together can sometimes yield a 9-bit
number) is called Integer Overflow. It can be a serious problem in C++ programs.

Whenever we do math with integers where the result could be larger in magnitude than the data
type has room for, we must guard against integer overflow. The result of an operation which
overflows is undefined. Usually it’s whatever actually did fit (the part that overflowed is lost), but
it may not be. When the type is signed, the overflow sometimes flips the sign bit (but not
always). Because of this, detecting an overflow cannot be done by simply comparing the result
of the operation to the limit:

// this will NOT work!


char tilde = 126;
char three = 51;
if (tilde + three > INT8_MAX) {
// handle overflow
}

126 + 51 = -79 for signed 8-bit integers (e.g. char). And -79 is not > 127.

So, how can you do it?

That is left as an exercise for the student.

Common questions

Powered by AI

To handle integer overflow in C++, certain strategies should be implemented. One recommended approach is to use division expressions before multiplication to check if the product will exceed the maximum value of the integer type. Similarly, for addition, one should use subtraction expressions with INT32_MAX or INT32_MIN to preemptively guard against overflow. This involves establishing conditions based on the operands to predict if an overflow condition will occur, rather than directly comparing the operation's result to the integer limits, as such a comparison may not effectively detect overflow .

Constructing test cases should start with examining the specific requirements and conditions laid out for each function. These guide the types of input and expected outcomes needed. Critical coverage should include normal operational cases, edge cases such as boundaries (e.g., zero, negatives where applicable), and invalid inputs that should raise exceptions. Understanding these conditions ensures test cases validate the correct functionality comprehensively and highlight issues effectively. Testing should expose logical and computational errors under various conditions .

In C++, the signedness of an integer affects its range as signed integers reserve one bit for the sign, hence they can represent both positive and negative values, leading to a smaller positive range compared to unsigned integers which only store positive values. For example, a signed 8-bit integer ranges from -128 to 127, while an unsigned one ranges from 0 to 255. Overflow behavior differs as well; signed overflow may flip the sign bit, causing unintended results, whereas unsigned overflow simply loses the overflowed portion without affecting the overall magnitude representation .

When debugging multiple functions, it is effective to address each function individually by first resolving syntax errors, which should be rectified sequentially starting with the first error identified by the compiler. Next, one should use test cases to match the specification requirements and think critically about potential edge cases. Employing debug logs and inspecting error messages can provide insights into unresolved errors. Correctly understanding and using tools and methods specific to the language, like exception handling for overflow in C++, can prevent thorough debugging slips .

The GoodDinner function evaluates if the number of pizzas consumed qualifies as a 'good dinner.' If it is not a weekend, it checks if the number of pizzas eaten is between 10 and 20, inclusive. If it is the weekend, there is no upper limit on the number of pizzas, so any number of pizzas 10 or more would return true. This logical structure ensures the function adheres to its parameter-based conditions without throwing exceptions .

Maintaining academic integrity is crucial because it ensures the learning objectives of the assignment are met by the individual student without external influences. The guidelines explicitly prohibit sharing code or solutions with peers to uphold the values of honesty and fairness in academic work. Violations should be reported, as integrity underpins the legitimacy of academic qualifications and ensures that students acquire the necessary skills and understanding individually .

Compiler error messages and warnings are critical in the debugging process as they directly point out where the syntactic or potential logical issues reside in the code. Addressing compiler errors should be the initial step, as they inhibit successful compilation. Once syntax errors are resolved, warnings, while not blocking compilation, provide insights into potential inefficiencies or logical fallacies. Resolving these often helps clean and optimize the code, ensuring adherence to best practices and avoiding future bugs .

2's complement is a binary system widely used in computers to represent negative numbers. It simplifies the hardware design for arithmetic operations because it allows for negative numbers to be added directly without separate logic for addition and subtraction. In 2’s complement, negative numbers are generated by inverting all bits of the number's absolute binary representation and adding one to the least significant bit. This results in overflow conditions where the sign bit may flip, reflecting changes in sign due to overflow, thus facilitating efficient arithmetic processing and overflow detection .

The function SumIsEven should return true if the sum of the two integer inputs is even and false if it is odd. It should not throw exceptions and handle typical integer inputs seamlessly. Edge cases, such as both inputs being zero (sum remains even) or dealing with negative integers, should not affect the correctness as these are checked by simply verifying the parity of the result (sum % 2 == 0).

The SmarterSection function calculates the performance of each section by determining the percentage of students who answered correctly. The function returns true if Section A's performance percentage is strictly greater than that of Section B. Importantly, the function should handle invalid arguments by throwing std::invalid_argument exceptions. This includes cases where total students may be zero, resulting in division by zero, or when the number of correct answers exceeds the total number of students .

You might also like