CSE-111编程讲解、辅导C++编程语言、C/C++程序调试 讲解留学生Processing|辅导Web开发
- 首页 >> Matlab编程 CSE-111 • Spring 2021 • Program 1 • Overloading and operators
1. Using C++11/14/17 (20?)
All programming in this course will be done C++ style, not C style, as shown in the
following table.
Do not use : Instead, use :
char* strings
C arrays
, ,
pointers
union inheritance
Include only C++11/14/17 header files where feasible : using namespace std;
Include files only when C++-style files are unavailable. Include h> files from C only when an appropriate files is unavailable. Use the
script cpplint.py.perl (a wrapper for cpplint.py) to check style.
2. Overview
This assignment will involve overloading basic integer operators to perform arbitrary
precision integer arithmetic in the style of dc(1). Your class bigint will intermix
arbitrarily with simple integer arithmetic.
To begin read the man(1) page for the command dc(1) :
man -s 1 dc
A copy of that page is also in this directory. Your program will use the standard dc
as a reference implemention and must produce exactly the same output for the
commands you have to implement :
+-*/%^cdfpq
If you have any questions as to the exact format of your output, just run dc(1) and
make sure that, for the operators specified above, your program produces exactly
the same output. A useful program to compare output from your program with that
of dc(1) is diff(1), which compares the contents of two files and prints only the differences.
Also look in the subdirectory misc/ for some examples.
See also :
• dc (computer program)
https://en.wikipedia.org/wiki/Dc_(computer_program)
• dc, an arbitrary precision calculator
https://www.gnu.org/software/bc/manual/dc-1.05/html_mono/dc.html
3. Implementation strategy
As before, you have been given starter code.
(a) Makefile, debug, and util If you find you need a function which does not properly
belong to a given module, you may add it to util.
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 2 of 5
(b) The module scanner reads in tokens, namely a NUMBER, an OPERATOR, or SCANEOF.
Each token returns a token_t, which indicates what kind of token it is (the
terminal_symbol symbol), and the string lexinfo associated with the token.
Only in the case of a number is there more than one character. Note that on
input, an underscore (_) indicates a negative number. The minus sign (-) is
reserved only as a binary operator. The scanner also has defined a couple of
operator<< for printing out scanner results in debug mode.
(c) The main program main.cpp, has been implemented for you. For the six binary
arithmetic functions, the right operand is popped from the stack, then the left
operand, then the result is pushed onto the stack.
(d) The module iterstack can not just be the STL stack, since we want to iterate
from top to bottom, and the STL stack does not have an iterator. A stack
depends on the operations back(), push_back(), and pop_back() in the underlying
container. We could use a vector, a deque, or just a list, as long as the requisite
operations are available.
4. Class bigint
Then we come to the most complex part of the assignment, namely the class bigint.
Operators in this class are heavily overloaded.
(a) Most of the functions take a arguments of type const bigint&, i.e., a constant
reference, for the sake of efficiency. But they have to return the result by
value.
(b) The operator<< can’t be a member since its left operand is an ostream, so we
make it a friend, so that it can see the innards of a bigint. Note now dc prints
really big numbers.
(c) The relational operators == and < are coded individually as member functions.
The others, !=, <=, >, and >= are defined in terms of the essential two.
(d) All of the functions of bigint only work with the sign, using ubigint to do the
actual computations. So bigint::operator+ and bigint::operator- will check
the signs of the two operands then call ubigint::operator+ or ubigint::operator-,
as appropriate, depending on the relative signs and magnitudes. The
multiplication and division operators just call the corresponding ubigint operators,
then adjust the resulting sign according to the rule of signs.
5. Class ubigint
Class ubigint implements unsigned large integers and is where the computational
work takes place. Class bigint takes care of the sign. Now we turn to the representation
of a ubigint, which will be represented by vector of bytes.
(a) Replace the declaration
using unumber = unsigned long;
unumber uvalue {};
with
using ubigvalue_t = vector;
ubigvalue_t ubig_value;
in the header file. The type uint8_t is an unsigned 8-bit integer
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 3 of 5
defined in.
(b) In storing the big integer, each digit is kept as an integer in the range 0 to 9 in
an element of the vector. Since the arithmetic operators add and subtract
work from least significant digit to most significant digit, store the elements of
the vector in the same order. That means, for example, that the number 4629
would be stored in a vector v as : v3 = 4, v2 = 6, v1 = 2, v0 = 9. In other words,
if a digit’s value is d × 10
k
, then vk = d.
(c) In order for the comparisons to work correctly, always store numbers in a
canonical form : After computing a value from any one of the six arithmetic
operators, always trim the vector by removing all high-order zeros :
while (size() > 0 and back() == 0) pop_back();
Zero should be represented as a vector of zero length and a positive sign.
(d) The scanner will produce numbers as strings, so scan each string from the end
of the string, using a const_reverse_iterator (or other means) from the end of
the string (least significant digit) to the beginning of the string (most signifi-
cant digit) using push_back to append them to the vector.
6. Implementation of Operators
(a) For bigint::operator+, check to see if the signs are the same or different. If
the same, call ubigint::operator+ to compute the sum, and set the result sign
as appopriate. If the signs are different, call ubigint::operator- with the
larger number first and the smaller number second. The sign is the sign of the
larger number.
(b) The operator bigint::operator- should perform similarly. If the signs are different,
it uses ubigint::operator+ but if the same, it uses ubigint::operator-.
(c) To implement ubigint::operator+, create a new ubigint and proceed from the
low order end to the high order end, adding digits pairwise. For any sum >=
10, take the remainder and add the carry to the next digit. Use push_back to
append the new digits to the ubigint. When you run out of digits in the
shorter number, continue, matching the longer vector with zeros, until it is
done. Make sure the sign of 0 is positive.
(d) To implement ubigint::operator-, also create a new empty vector, starting
from the low order end and continuing until the high end. If the left digit is
smaller than the right digit, the subtraction will be less than zero. In that
case, add 10 to the digit, and set the borrow to the next digit to −1. After the
algorithm is done, pop_back all high order zeros from the vector before returning
it. Make sure the sign of 0 is positive.
(e) To implement operator==, check to see if the signs are the same and the
lengths of the vectors are the same. If not, return false. Otherwise run down
both vectors and return false as soon a difference is found. Otherwise return
true.
(f) To implement operator<, remember that a negative number is less than a positive
number. If the signs are the same, for positive numbers, the shorter one is
less, and for negative nubmers, the longer one is less. If the signs and lengths
are the same, run down the parallel vectors from the high order end to the low
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 4 of 5
order end. When a difference is found, return true or false, as appropriate. If
no difference is found, return false.
(g) Implement function bigint::operator*, which uses the rule of signs to determine
the result. The number crunching is delegated to ubigint::operator*,
which produces the unsigned result.
(h) Multiplication in ubigint::operator* proceeds by allocating a new vector
whose size is equal to the sum of the sizes of the other two operands. If u is a
vector of size m and v is a vector of size n, then in O(mn) speed, perform an
outer loop over one argument and an inner loop over the other argument,
adding the new partial products to the product p as you would by hand. The
algorithm can be described mathematically as follows :
p ← Φ
for i ∈ [0, m) :
c ← 0
for j ∈ [0, n) :
d ← pi+ j + uivj + c
pi+ j ← d rem 10
c ← d ÷ 10
pi+n ← c
Note that the interval [a, b) refers to the set {x|a ≤ x < b}, i.e., to a half-open
interval including a but excluding b. In the same way,apair of iterators in
C++ bound an interval.
(i) Long division is complicated if done correctly. See a paper by P. Brinch
Hansen, ‘‘Multiple-length division revisited : A tour of the minefield’’, Software
— Practice and Experience 24, (June 1994), 579–601. Algorithms 1 to 12 are
on pages 13–23, Note that in Pascal, array bounds are part of the type, which
is not true for vectors in C++.
multiple-length-division.pdf
http://brinch-hansen.net/papers/1994b.pdf
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.5815
(j) The function divide as implemented uses the ancient Egyptian division algorithm,
which is slower than Hansen’s Pascal program, but is easier to understand.
Replace the long values in it by vector. The logic is shown
also in misc/divisioncpp.cpp. The algorithm is rather slow, but the big-O
analysis is reasonable.
(k) Modify operator<<, first just to print out the number all in one line. You will
need this to debug your program. When you are finished, make it print numbers
in the same way as dc(1) does.
(l) The pow function uses other operations to raise a number to a power. If the
exponent does not fit into a single long print an error message, otherwise do
the computation. The power function is not a member of either bigint or ubigint,
and is just considered a library function that is implemented using more
primitive operations.
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 5 of 5
7. Memory leak and other problems
Make sure that you test your program completely so that it does not crash on a Segmentation
Fault or any other unexpected error. Since you are not using pointers,
and all values are inline, there should be no memory leak. Use valgrind(1) to check
for and eliminate uninitialized variables and memory leak.
If your program is producing strange output or segmentation faults, use gdb(1) and
the debug macros in the debug module of the code/ subdirectory.
8. Version of g++
The code must compile and run using g++ on unix.ucsc.edu, regardless of whether it
runs elsewhere. When this document was formatted that was :
bash-$ which g++
/opt/rh/devtoolset-8/root/usr/bin/g++
bash-$ g++ --version | head -1
g++ (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3)
bash-$ echo $(uname -sp) - $(hostname) - $(date)
Linux x86_64 - unix6.lt.ucsc.edu - Fri Mar 26 00:01:02 PDT 2021
9. What to submit
Submit source files and only source files : Makefile, README, and all of the header
and implementation files necessary to build the target executable. If gmake does not
build ydc your program can not be tested and you lose 1/2 of the points for the
assignment. Use checksource on your code to verify basic formatting.
Look in the .score/ subdirectory for instructions to graders. Read Syllabus/pairprogramming/
and also submit PARTNER if you are doing pair programming. Either
wa y submit the README described therein.
10. Et cetera (και` τα` ‘´ετερα).
The accuracy of the Unix utility dc(1) can be checked by :
echo ’82 43/25 43+65P80P82P73P76P32P70P79P79P76P10P’ | dc
1. Using C++11/14/17 (20?)
All programming in this course will be done C++ style, not C style, as shown in the
following table.
Do not use : Instead, use :
char* strings
C arrays
pointers
union inheritance
Include only C++11/14/17 header files where feasible : using namespace std;
Include
script cpplint.py.perl (a wrapper for cpplint.py) to check style.
2. Overview
This assignment will involve overloading basic integer operators to perform arbitrary
precision integer arithmetic in the style of dc(1). Your class bigint will intermix
arbitrarily with simple integer arithmetic.
To begin read the man(1) page for the command dc(1) :
man -s 1 dc
A copy of that page is also in this directory. Your program will use the standard dc
as a reference implemention and must produce exactly the same output for the
commands you have to implement :
+-*/%^cdfpq
If you have any questions as to the exact format of your output, just run dc(1) and
make sure that, for the operators specified above, your program produces exactly
the same output. A useful program to compare output from your program with that
of dc(1) is diff(1), which compares the contents of two files and prints only the differences.
Also look in the subdirectory misc/ for some examples.
See also :
• dc (computer program)
https://en.wikipedia.org/wiki/Dc_(computer_program)
• dc, an arbitrary precision calculator
https://www.gnu.org/software/bc/manual/dc-1.05/html_mono/dc.html
3. Implementation strategy
As before, you have been given starter code.
(a) Makefile, debug, and util If you find you need a function which does not properly
belong to a given module, you may add it to util.
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 2 of 5
(b) The module scanner reads in tokens, namely a NUMBER, an OPERATOR, or SCANEOF.
Each token returns a token_t, which indicates what kind of token it is (the
terminal_symbol symbol), and the string lexinfo associated with the token.
Only in the case of a number is there more than one character. Note that on
input, an underscore (_) indicates a negative number. The minus sign (-) is
reserved only as a binary operator. The scanner also has defined a couple of
operator<< for printing out scanner results in debug mode.
(c) The main program main.cpp, has been implemented for you. For the six binary
arithmetic functions, the right operand is popped from the stack, then the left
operand, then the result is pushed onto the stack.
(d) The module iterstack can not just be the STL stack, since we want to iterate
from top to bottom, and the STL stack does not have an iterator. A stack
depends on the operations back(), push_back(), and pop_back() in the underlying
container. We could use a vector, a deque, or just a list, as long as the requisite
operations are available.
4. Class bigint
Then we come to the most complex part of the assignment, namely the class bigint.
Operators in this class are heavily overloaded.
(a) Most of the functions take a arguments of type const bigint&, i.e., a constant
reference, for the sake of efficiency. But they have to return the result by
value.
(b) The operator<< can’t be a member since its left operand is an ostream, so we
make it a friend, so that it can see the innards of a bigint. Note now dc prints
really big numbers.
(c) The relational operators == and < are coded individually as member functions.
The others, !=, <=, >, and >= are defined in terms of the essential two.
(d) All of the functions of bigint only work with the sign, using ubigint to do the
actual computations. So bigint::operator+ and bigint::operator- will check
the signs of the two operands then call ubigint::operator+ or ubigint::operator-,
as appropriate, depending on the relative signs and magnitudes. The
multiplication and division operators just call the corresponding ubigint operators,
then adjust the resulting sign according to the rule of signs.
5. Class ubigint
Class ubigint implements unsigned large integers and is where the computational
work takes place. Class bigint takes care of the sign. Now we turn to the representation
of a ubigint, which will be represented by vector of bytes.
(a) Replace the declaration
using unumber = unsigned long;
unumber uvalue {};
with
using ubigvalue_t = vector
ubigvalue_t ubig_value;
in the header file
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 3 of 5
defined in
(b) In storing the big integer, each digit is kept as an integer in the range 0 to 9 in
an element of the vector. Since the arithmetic operators add and subtract
work from least significant digit to most significant digit, store the elements of
the vector in the same order. That means, for example, that the number 4629
would be stored in a vector v as : v3 = 4, v2 = 6, v1 = 2, v0 = 9. In other words,
if a digit’s value is d × 10
k
, then vk = d.
(c) In order for the comparisons to work correctly, always store numbers in a
canonical form : After computing a value from any one of the six arithmetic
operators, always trim the vector by removing all high-order zeros :
while (size() > 0 and back() == 0) pop_back();
Zero should be represented as a vector of zero length and a positive sign.
(d) The scanner will produce numbers as strings, so scan each string from the end
of the string, using a const_reverse_iterator (or other means) from the end of
the string (least significant digit) to the beginning of the string (most signifi-
cant digit) using push_back to append them to the vector.
6. Implementation of Operators
(a) For bigint::operator+, check to see if the signs are the same or different. If
the same, call ubigint::operator+ to compute the sum, and set the result sign
as appopriate. If the signs are different, call ubigint::operator- with the
larger number first and the smaller number second. The sign is the sign of the
larger number.
(b) The operator bigint::operator- should perform similarly. If the signs are different,
it uses ubigint::operator+ but if the same, it uses ubigint::operator-.
(c) To implement ubigint::operator+, create a new ubigint and proceed from the
low order end to the high order end, adding digits pairwise. For any sum >=
10, take the remainder and add the carry to the next digit. Use push_back to
append the new digits to the ubigint. When you run out of digits in the
shorter number, continue, matching the longer vector with zeros, until it is
done. Make sure the sign of 0 is positive.
(d) To implement ubigint::operator-, also create a new empty vector, starting
from the low order end and continuing until the high end. If the left digit is
smaller than the right digit, the subtraction will be less than zero. In that
case, add 10 to the digit, and set the borrow to the next digit to −1. After the
algorithm is done, pop_back all high order zeros from the vector before returning
it. Make sure the sign of 0 is positive.
(e) To implement operator==, check to see if the signs are the same and the
lengths of the vectors are the same. If not, return false. Otherwise run down
both vectors and return false as soon a difference is found. Otherwise return
true.
(f) To implement operator<, remember that a negative number is less than a positive
number. If the signs are the same, for positive numbers, the shorter one is
less, and for negative nubmers, the longer one is less. If the signs and lengths
are the same, run down the parallel vectors from the high order end to the low
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 4 of 5
order end. When a difference is found, return true or false, as appropriate. If
no difference is found, return false.
(g) Implement function bigint::operator*, which uses the rule of signs to determine
the result. The number crunching is delegated to ubigint::operator*,
which produces the unsigned result.
(h) Multiplication in ubigint::operator* proceeds by allocating a new vector
whose size is equal to the sum of the sizes of the other two operands. If u is a
vector of size m and v is a vector of size n, then in O(mn) speed, perform an
outer loop over one argument and an inner loop over the other argument,
adding the new partial products to the product p as you would by hand. The
algorithm can be described mathematically as follows :
p ← Φ
for i ∈ [0, m) :
c ← 0
for j ∈ [0, n) :
d ← pi+ j + uivj + c
pi+ j ← d rem 10
c ← d ÷ 10
pi+n ← c
Note that the interval [a, b) refers to the set {x|a ≤ x < b}, i.e., to a half-open
interval including a but excluding b. In the same way,apair of iterators in
C++ bound an interval.
(i) Long division is complicated if done correctly. See a paper by P. Brinch
Hansen, ‘‘Multiple-length division revisited : A tour of the minefield’’, Software
— Practice and Experience 24, (June 1994), 579–601. Algorithms 1 to 12 are
on pages 13–23, Note that in Pascal, array bounds are part of the type, which
is not true for vectors in C++.
multiple-length-division.pdf
http://brinch-hansen.net/papers/1994b.pdf
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.5815
(j) The function divide as implemented uses the ancient Egyptian division algorithm,
which is slower than Hansen’s Pascal program, but is easier to understand.
Replace the long values in it by vector
also in misc/divisioncpp.cpp. The algorithm is rather slow, but the big-O
analysis is reasonable.
(k) Modify operator<<, first just to print out the number all in one line. You will
need this to debug your program. When you are finished, make it print numbers
in the same way as dc(1) does.
(l) The pow function uses other operations to raise a number to a power. If the
exponent does not fit into a single long print an error message, otherwise do
the computation. The power function is not a member of either bigint or ubigint,
and is just considered a library function that is implemented using more
primitive operations.
CSE-111 • Spring 2021 • Program 1 • Overloading and operators 5 of 5
7. Memory leak and other problems
Make sure that you test your program completely so that it does not crash on a Segmentation
Fault or any other unexpected error. Since you are not using pointers,
and all values are inline, there should be no memory leak. Use valgrind(1) to check
for and eliminate uninitialized variables and memory leak.
If your program is producing strange output or segmentation faults, use gdb(1) and
the debug macros in the debug module of the code/ subdirectory.
8. Version of g++
The code must compile and run using g++ on unix.ucsc.edu, regardless of whether it
runs elsewhere. When this document was formatted that was :
bash-$ which g++
/opt/rh/devtoolset-8/root/usr/bin/g++
bash-$ g++ --version | head -1
g++ (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3)
bash-$ echo $(uname -sp) - $(hostname) - $(date)
Linux x86_64 - unix6.lt.ucsc.edu - Fri Mar 26 00:01:02 PDT 2021
9. What to submit
Submit source files and only source files : Makefile, README, and all of the header
and implementation files necessary to build the target executable. If gmake does not
build ydc your program can not be tested and you lose 1/2 of the points for the
assignment. Use checksource on your code to verify basic formatting.
Look in the .score/ subdirectory for instructions to graders. Read Syllabus/pairprogramming/
and also submit PARTNER if you are doing pair programming. Either
wa y submit the README described therein.
10. Et cetera (και` τα` ‘´ετερα).
The accuracy of the Unix utility dc(1) can be checked by :
echo ’82 43/25 43+65P80P82P73P76P32P70P79P79P76P10P’ | dc