Thursday, 22 August 2019

Chapter 9 // Exercise 16 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 16



Define an input operator (>>) that reads monetary amounts with currency denominations, such as USD1.23 and DKK5.00, into a Money variable. Also define a corresponding output operator.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

int main()
{
 Money amount1(c_GBP, 3.34);
 amount1.printMoney();

 Money amount2(c_USD, 786.789);
 amount1.printMoney();

 //read in new money
 cin >> amount1;
 cin >> amount2;

 //print new amounts
 cout << amount1 << endl;
 cout << amount2 << endl;

 cout << "\nPress any key to quit...";
 _getch();

 return 0;
}

moneyClass.h

//----------------------------------//
// moneyClass.h
//----------------------------------//
// for calculations involving money
//----------------------------------//
#ifndef _MONEYCLASS_H_
#define _MONEYCLASS_H_

//INCLUDES//
#include <string>
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

//----------------------------------//
// ENUM: Currencies
//----------------------------------//
enum Currencies
{
 c_GBP,
 c_USD
};

//----------------------------------//
// CLASS: Money
//----------------------------------//
class Money
{
public:
 Money();
 Money(Currencies currency, float amount);
 ~Money();

 void printMoney();

 Currencies getCurrency() { return m_currency; }
 void changeCurrency(Currencies currency) { m_currency = currency; }
 long int   getOutPutMoney() { return m_outputMoney; }
 double     getInputMoney()  { return m_inputMoney; }

private:
 //variables//
 Currencies m_currency;
 long int m_outputMoney; //cents
 double m_inputMoney; //dollars

};

//OPERATOR OVERLOADS//
void operator+(Money& money1, Money& money2);
ostream& operator<<(ostream& os, Money& money);
istream& operator>>(istream& is, Money& money);

#endif // !_MONEYCLASS_H_


moneyClass.cpp

//----------------------------------//
// moneyClass.cpp
//----------------------------------//
// for calculations involving money
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

//VARIABLES//
double gbp2usd = 1.26;
double usd2gbp = 0.79;


Money::Money() {}

Money::Money(Currencies currency, float amount) 
{
 m_currency = currency;
 m_inputMoney = amount;
}

Money::~Money() {}

//print output as dollars
void Money::printMoney()
{
 m_outputMoney = round(m_inputMoney * 100.0f);

 switch (m_currency)
 {
 case c_USD:
  cout << "-----USD----" << endl;
  cout << "Dollars: $" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Cents: " << fixed << setprecision(0) << m_outputMoney << endl;
  break;
 case c_GBP:
  cout << "-----GBP----" << endl;
  cout << "Pounds: £" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Pennies: " << fixed << setprecision(0) << m_outputMoney << "p" << endl;
  break;
 default:
  cout << "Bad output";
  break;
 }
}

//OPERATOR OVERLOADS//

//add currencies together - converts money if not the same
void operator+(Money& money1, Money& money2)
{
 cout << "\n";
 if (money1.getCurrency() == money2.getCurrency())
  //currencies are the same, no conversion needed, just add
  cout << fixed << setprecision(2) << money1.getInputMoney() + money2.getInputMoney();
 else if (money1.getCurrency() == c_GBP && money2.getCurrency() == c_USD)
 {
  //return pounds
  cout << fixed << setprecision(2) << "£" << (money2.getInputMoney() * usd2gbp) + money1.getInputMoney() << endl;
 }
 else if (money1.getCurrency() == c_USD && money2.getCurrency() == c_GBP)
 {
  //return dollars
  cout << fixed << setprecision(2) << "$" << (money2.getInputMoney() * gbp2usd) + money1.getInputMoney() << endl;
 }
}

//print out money
ostream& operator<<(ostream& os, Money& money)
{
 switch (money.getCurrency())
 {
 case c_GBP:
  return os << fixed << setprecision(2) << "\n£" << money.getInputMoney() << endl;
  break;
 case c_USD:
  return os << fixed << setprecision(2) << "\n$" << money.getInputMoney() << endl;
  break;
 default:
  return os << "Bad output";
  break;
 }
}

//read money into a Money variable
istream& operator>>(istream& is, Money& money)
{
 cout << "Please enter currency, followed by amount. For example GBP3.45" << endl;
 cout << "Currencies available: GBP || USD" << endl;

 string getMoney;
 cin >> getMoney;

 string denomination, amount;
 double mon;

 for (int i = 0; i < getMoney.size(); ++i)
 {
  if (i < 3)
   denomination += getMoney[i];
  else
   amount += getMoney[i];
 }

 //convert string to double
 mon = stod(amount);

 //update money variable
 if (denomination == "GBP")
 {
  money = Money(c_GBP, mon);
  money.changeCurrency(c_GBP);
 }
 else if (denomination == "USD")
 {
  money = Money(c_USD, mon);
  money.changeCurrency(c_USD);
 }
 else
 {
  cout << "bad input.";
 }

 return is;
}

My example is omitting some error checking due to time constraints so don't put in any illegal values otherwise it'll break however it does what he has asked for. When using cin >> with a Money variable it allows the user to input a new monetary value as well as change the currency. The << was already implemented in the last exercise. I also learnt in this exercise that there is a whole load of functions in the standard library for converting strings to almost any type of decimal. Bless the standard library.

Wednesday, 21 August 2019

Chapter 9 // Exercise 15 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 15



Refine the Money class by adding a currency (given as a constructor argument). Accept a floating-point initializer as long as it can be exactly represented as a long int. Don't accept illegal operations. For example, Money*Money doesn't make sense, and USD1.23+DKK5.00 makes sense only if you provide a conversion table defining the conversion factor between U.S. dollars (USD) and Danish kroner (DKK).

...I literally have no idea what he wants me to do here. So we add a currency in the constructor, say GBP as a string along with a floating-point value. We've already been doing that but now we have to to do a check to ensure that it will fit in a long int. A long int is 32 bits in size and a double is 64 bits. (If anyone is interested, in a programming interview I was asked about the size of a struct and it contained all the fundamental types, so it's worth learning the sizes of each type). A float is 32 bits so perhaps we should change m_dollars to a float instead. The worst that would happen if we didn't is that we would lose some bits as they would get truncated to fit. I don't understand what he means by illegal operations though. Does he mean, don't allow Money*Money in the initialiser? It wouldn't compile anyway in the code as there is no definition for it. And for USD1.23+DKK5.00 where is this called? By the programmer writing the code or is it an argument passed to the program via input from the user? If it was the latter, I suppose you would take it in as a string and parse each value, then pass the currency and value to the money class to be constructed with that value. You can construct new classes at run time but it's far beyond what's been taught in the book so far. This is not well constructed exercise.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

int main()
{
 Money gbp(c_GBP, 3.34);
 gbp.printMoney();

 Money usd(c_USD, 786.789);
 usd.printMoney();

 gbp + usd;
 usd + gbp;

 cout << "\nPress any key to quit...";
 _getch();

 return 0;
}

moneyClass.h

//----------------------------------//
// moneyClass.h
//----------------------------------//
// for calculations involving money
//----------------------------------//
#ifndef _MONEYCLASS_H_
#define _MONEYCLASS_H_

//INCLUDES//
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

//----------------------------------//
// ENUM: Currencies
//----------------------------------//
enum Currencies
{
 c_GBP,
 c_USD
};

//----------------------------------//
// CLASS: Money
//----------------------------------//
class Money
{
public:
 Money(Currencies currency, float amount);
 ~Money();

 void printMoney();

 Currencies getCurrency()    { return m_currency; }
 long int   getOutPutMoney() { return m_outputMoney; }
 double     getInputMoney()  { return m_inputMoney; }

private:
 //variables//
 Currencies m_currency;
 long int m_outputMoney; //cents
 double m_inputMoney; //dollars

};

//OPERATOR OVERLOADS//
void operator+(Money& money1, Money& money2);
ostream& operator<<(ostream& os, Money& money);

#endif // !_MONEYCLASS_H_


moneyClass.cpp

//----------------------------------//
// moneyClass.cpp
//----------------------------------//
// for calculations involving money
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

//VARIABLES//
double gbp2usd = 1.26;
double usd2gbp = 0.79;

Money::Money(Currencies currency, float amount) 
{
 m_currency = currency;
 m_inputMoney = amount;
}

Money::~Money() {}

//print output as dollars
void Money::printMoney()
{
 m_outputMoney = round(m_inputMoney * 100.0f);

 switch (m_currency)
 {
 case c_USD:
  cout << "-----USD----" << endl;
  cout << "Dollars: $" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Cents: " << fixed << setprecision(0) << m_outputMoney << endl;
  break;
 case c_GBP:
  cout << "-----GBP----" << endl;
  cout << "Pounds: £" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Pennies: " << fixed << setprecision(0) << m_outputMoney << "p" << endl;
  break;
 default:
  cout << "Bad output";
  break;
 }
}

//OPERATOR OVERLOADS//

//add currencies together - converts money if not the same
void operator+(Money& money1, Money& money2)
{
 cout << "\n";
 if (money1.getCurrency() == money2.getCurrency())
  //currencies are the same, no conversion needed, just add
  cout << fixed << setprecision(2) << money1.getInputMoney() + money2.getInputMoney();
 else if (money1.getCurrency() == c_GBP && money2.getCurrency() == c_USD)
 {
  //return pounds
  cout << fixed << setprecision(2) << "£" << (money2.getInputMoney() * usd2gbp) + money1.getInputMoney() << endl;
 }
 else if (money1.getCurrency() == c_USD && money2.getCurrency() == c_GBP)
 {
  //return dollars
  cout << fixed << setprecision(2) << "$" << (money2.getInputMoney() * gbp2usd) + money1.getInputMoney() << endl;
 }
}

//print out money
ostream& operator<<(ostream& os, Money& money)
{
 switch (money.getCurrency())
 {
 case c_GBP:
  return os << "\n£" << money.getInputMoney() << endl;
  break;
 case c_USD:
  return os << "\n$" << money.getInputMoney() << endl;
  break;
 default:
  return os << "Bad output";
  break;
 }
}

I'm still not entirely sure if this is what he wanted us to do but it takes in a currency (and you can add more to the enum, just provide converters in moneyClass.cpp). I even added some operator overloads to print out the money and add different currencies together.

Tuesday, 20 August 2019

Chapter 9 // Exercise 14 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 14



Design and implement a Money class for calculations involving dollars and cents where arithmetic has to be accurate to the last cent using the 4/5 rounding rule (.5 of a cent rounds up; anything less than .5 rounds down). Represent a monetary amount as a number of cents in a long int, but input and output as dollars and cents, e.g., $123.45. Do not worry about amounts that don't fit into a long int.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

int main()
{
 double cents, dollars;
 cout << "$";
 cin >> dollars;

 cents = dollars * 100.0f;
 cents = round(cents);

 cout << "Dollars: $" << fixed << setprecision(2) << dollars << endl;
 cout << "Cents: " << fixed << setprecision(0) << cents << endl;

 system("pause");
 return 0;
}

My first attempt I felt I had not done it correctly as it seemed a little too simple. It was the rounding rule that got me. Where did we need the code to round? When converting dollars to cents you just multiply it by 100. Then divide it by 100 to convert it back to dollars. I'm guessing he means that if someone inputs $2.298 he wants that figure rounded to $2.30 then stored as cents in a long int. I created a simple rounding program as seen above. This does exactly what he specifies. I then re-jigged it into the class format below as we expand on it in the next few exercises.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

int main()
{
 Money money;
 money.menu();

 return 0;
}

moneyClass.h

//----------------------------------//
// moneyClass.h
//----------------------------------//
// for calculations involving dollars
// and cents
//----------------------------------//
#ifndef _MONEYCLASS_H_
#define _MONEYCLASS_H_

//INCLUDES//
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

//----------------------------------//
// CLASS: Money
//----------------------------------//
class Money
{
public:
 Money();
 ~Money();

 void menu();

private:
 void getInput();
 void printDollars();

 void convertToCents();

 //variables//
 long int m_cents;
 double m_dollars;

};

#endif // !_MONEYCLASS_H_


moneyClass.cpp

//----------------------------------//
// moneyClass.cpp
//----------------------------------//
// for calculations involving dollars
// and cents
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

Money::Money() {}

Money::~Money() {}

//main menu
void Money::menu()
{
 cout << "-----Money Class----" << endl;

 getInput();
 convertToCents();
 printDollars();
}

//get input as $0.00
void Money::getInput()
{
 double input;
 cout << "Please enter dollars: \n$";
 cin >> input;

 m_dollars = input;
}

//convert dollars to cents
void Money::convertToCents()
{
 //100 cents == $1
 m_cents = round(m_dollars * 100.0f);
}

//print output as dollars
void Money::printDollars()
{
 cout << "Dollars: $" << fixed << setprecision(2) << m_dollars << endl;
 cout << "Cents: " << fixed << setprecision(0) << m_cents << endl;

 //return to main menu
 system("pause");
 system("CLS");
 m_cents = m_dollars = 0;
 menu();
}

A lot of people seem to go a bit crazy when it comes to rounding. The standard library provides a function called round() which generally adheres to the 4/5 rounding rule so there's no need to implement it yourself. Also, I used a float at first to store the currency however it came with some issues to I switched to a double to store the initial dollars in. I then just manipulated the output using cout functions to make them specific to certain decimal points.

Monday, 19 August 2019

Chapter 9 // Exercise 13 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 13



Design and implement a rational number class, Rational. A rational number has two parts: a numerator and a denominator, for example, 5/6 (five-sixths, also known as approximately 0.83333). Look up the definition if you need to. Provide assignment, addition, subtraction, multiplication, division and equality operators. Also, provide a conversion to double. Why would people want to use a Rational class?

This one took me a while as I planned out exactly how to go around it. Bjarne made the exercise vague enough for it to be interpreted a number of ways (pun intended). This was not a hard exercise, however it did make me think about class structure and the time was spent rearranging functions. This isn't perfect as I've omitted quite a few error checks, which you obviously wouldn't do if the public were to use this. 

I probably spent more time than is necessary on formatting but I just wanted it to look pretty. We could have gone the whole calculator route as well and done a new version which took in a string of say "4:5 * 7:5" with ':' denoting the separator as we'd need that sign for division however I instead left it for the user to just implement what they'd like in code.

This kind of class would be very useful to people like me who get flustered when they see things like "5/16ths of inch". I've never really been that great at working with fractions so something that works it out for you is quite useful. However, you could just use Google...like I do.

As I've used quite a few files, here is the full code drop:
https://github.com/l-paz91/principles-practice/tree/master/Chapter%209/Exercise%2013

EDIT 03/12/2019 - The link above is a link to a new version that I completed today. To be honest I can't even remember what I did now for the original but it sounds stupidly complicated.

I found the following sites useful:
Rational Numbers
Finding Greatest Common Divisor