Critique my Federal Tax Calculation Program

This is a textbook exercise and here is the question verbatim

Write a program that can be used to calculate the federal tax. The tax is calculated as follows: For single people, the standard exemption is $4,000; for married people, the standard exemption is $7,000. A person can also put up to 6% of his or her gross income in a pension plan. The tax rates are as follows: If the taxable income is:

1. Between $0 and $15,000, the tax rate is 15%.
2. Between $15,001 and $40,000, the tax is $2,250 plus 25% of the taxable income over $15,000.
3. Over $40,000, the tax is $8,460 plus 35% of the taxable income over $40,000.

Prompt the user to enter the following information

1. Marital Status
2. If the marital status is "married," ask for the number of children under the age of 14
3. Gross salary (If the marital status is "married" and both spouses have income, enter the combined salary.)
4. Percentage of gross income contributed to a pension fund

Your program must consist of at least the following functions:
a. Function getData: This function asks the user to enter the relevant data.
b. Function taxAmount: This function computes and returns the tax owed.

To calculate the taxable income, subtract the sum of the standard exemption, the amount contributed to a pension plan, and the personal exemption, which is $1,500 per person. (Note that if a married couple has two children under the age of 14, then the personal exemption is $1,500 * 4 = $6,000.)


This is my solution code. The problem is that my getData function is too big. I included a lot of input validations to make it more foolproof, but it makes the code look really bad and disorganized. How can I improve it?
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <iostream>
#include <iomanip>
#include <cctype>
#include <cmath>

// Record to Store Tax Data for Household
struct TaxDataSet
{
    bool isMarried;
    int numberOfChildren;
    double grossSalary;
    double pensionContribution;
};

// Constant Variables for Salary Ranges
const double LOWER_BOUND_INCOME = 15000.00;
const double UPPER_BOUND_INCOME = 40000.00;

// Constant Variables for Default Tax Amount and Tax Rates
const double DEFAULT_TAX_0_15000 = 0.00;
const double TAX_RATE_0_15000 = 0.15;
const double DEFAULT_TAX_15001_40000 = 2250.00;
const double TAX_RATE_15001_40000 = 0.25;
const double DEFAULT_TAX_40000 = 8460.00;
const double TAX_RATE_40000 = 0.35;

// Constant Variables for Exemption
const double EXEMPTION_SINGLE = 4000.00;
const double EXEMPTION_MARRIED = 7000.00;
const double EXEMPTION_PERSONAL = 1500.00;

// Constant Variable for Pension Contribution in Percentage
const double PENSION_CONTRIBUTION_LIMIT = 0.06;

// Constant Variable for Children Age Limit
const int CHILDREN_AGE_LIMIT = 14;


// Function Prototypes
TaxDataSet getData ( TaxDataSet dataSet );
double taxAmount ( TaxDataSet dataSet );


int main() {

    TaxDataSet dataSet;

    dataSet = getData ( dataSet );

    std::cout << std::endl << std::setprecision ( 2 ) << std::fixed;

    std::cout << "The Federal Tax Liability for Your Household is $" << taxAmount ( dataSet ) << std::endl;

    return 0;
}

// This function asks the user to enter the relevant data. Returns TaxDataSet struct
TaxDataSet getData ( TaxDataSet dataSet ) {

    char maritalStatus;
    std::cout << "Enter Your Marital Status ( 'M' - Married or 'S' - Single ): ";
    std::cin >> maritalStatus;

    maritalStatus = toupper ( maritalStatus );  // Remove case-sensitivity of user input

    while ( maritalStatus != 'M' && maritalStatus != 'S' ) {    // Input Validation Loop
        std::cin.clear ();
        std::cin.ignore ( 100 , '\n' );

        std::cout << "Enter Valid Marital Status ( 'M' - Married or 'S' - Single ): ";
        std::cin >> maritalStatus;
        maritalStatus = toupper ( maritalStatus );
    }

    switch ( maritalStatus ) {

        case 'M':
            dataSet.isMarried = true;

            std::cout << "Enter the Number of Children Under the Age of " << CHILDREN_AGE_LIMIT << " in Your Household: ";
            std::cin >> dataSet.numberOfChildren;

            while ( !std::cin || dataSet.numberOfChildren < 0 ) {   // Input Validation Loop
                std::cin.clear ();
                std::cin.ignore ( 100 , '\n' );

                std::cout << "Enter Valid Number of Children: ";
                std::cin >> dataSet.numberOfChildren;
            }

            break;

        case 'S':
            dataSet.isMarried = false;
            break;
    }

    std::cout << "Enter Your Gross Salary: $";
    std::cin >> dataSet.grossSalary;

    while ( !std::cin ) {     // Input Validation Loop
        std::cin.clear ();
        std::cin.ignore ( 100 , '\n' );

        std::cout << "Enter Valid Gross Salary: $";
        std::cin >> dataSet.grossSalary;
    }

    double spouseSalary;

    if ( dataSet.isMarried ) {
        std::cout << "Enter Your Spouse's Gross Salary: $";
        std::cin >> spouseSalary;

        while ( !std::cin ) {       // Input Validation Loop
            std::cin.clear ();
            std::cin.ignore ( 100 , '\n' );

            std::cout << "Enter Valid Gross Salary: $";
            std::cin >> spouseSalary;
        }

        dataSet.grossSalary += spouseSalary;
    }

    std::cout << "Enter the Percentage of Gross Income Contributed to Pension Fund:    %" << "\b\b\b\b";
    std::cin >> dataSet.pensionContribution;

    while ( dataSet.pensionContribution > PENSION_CONTRIBUTION_LIMIT * 100 || !std::cin ) {
        std::cin.clear ();
        std::cin.ignore ( 100 , '\n' );

        std::cout << "Your pension contribution can't be higher than " << PENSION_CONTRIBUTION_LIMIT * 100
                  << "%. Try again:    %" << "\b\b\b\b";
        std::cin >> dataSet.pensionContribution;
    }

    dataSet.pensionContribution /= 100;     // Convert percent to decimal

    return dataSet;
}


// This function computes and returns the tax owed
double taxAmount ( TaxDataSet dataSet ) {

    double taxableIncome = dataSet.grossSalary;

    taxableIncome -= ( taxableIncome * dataSet.pensionContribution );

    if ( dataSet.isMarried )
        taxableIncome = taxableIncome - EXEMPTION_MARRIED - ( ( 2 + dataSet.numberOfChildren ) * EXEMPTION_PERSONAL );
    else
        taxableIncome = taxableIncome - EXEMPTION_SINGLE - EXEMPTION_PERSONAL;

    if ( taxableIncome > 0 ) {
        if ( taxableIncome <= LOWER_BOUND_INCOME )
            return ( DEFAULT_TAX_0_15000 + taxableIncome * TAX_RATE_0_15000 );
        else if ( taxableIncome <= UPPER_BOUND_INCOME )
            return ( DEFAULT_TAX_15001_40000 + taxableIncome * TAX_RATE_15001_40000 );
        else
            return ( DEFAULT_TAX_40000 + taxableIncome * TAX_RATE_40000 );
    }
    else
        return 0.00;
}
Last edited on
The problem is that my getData function is too big. I included a lot of input validations to make it more foolproof
Welcome to "real world" programming. In real programs, the functionality is a small part of the total number of lines of code. Most of the code is checking and recovering from errors.

When validating input, it's cleaner to break out of the middle of the loop. The general pattern I use is:
1
2
3
4
5
while (true) {
    prompt for input
    if input is valid then break
    print error message
}


So to get the marital status, you'd do this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    while (true) {
        std::cout << "Enter Your Marital Status ( 'M' - Married or 'S' - Single ): ";
        std::cin >> maritalStatus;

        maritalStatus = toupper ( maritalStatus );  // Remove case-sensitivity of user input

        if (maritalStatus == 'M' || maritalStatus == 'S' ) {    // Input Validation Loop
            break;
        }

        // Input is invalid
        std::cin.clear ();
        std::cin.ignore ( 100 , '\n' );
    }


The code that gets the salaries is identical except for the prompts, so factor that out:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
double getDouble(const char *prompt, const char *errorMsg)
{
    double result;
    std::cout << prompt;
    while (true) {
        if (std::cin >> result) {
            return result;
        }
        std::cout << errorMsg;
    }
}
...
    dataSet.grossSalary = getDouble("Enter Your Gross Salary: $",
                                    "Enter Valid Gross Salary: $");

    if ( dataSet.isMarried ) {
        double spouseSalary = getDouble("Enter Your Spouse's Gross Salary: $",
                                 "Enter Valid Gross Salary: $");
        dataSet.grossSalary += spouseSalary;
    }


Write many small functions.

As far as possible, avoid writing loops in your code;
particularly loops which contain the jump statements break, continue or goto.

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
#include <iostream>
#include <string>

struct TaxDataSet
{
    bool isMarried;
    int numberOfChildren;
    double grossSalary;
    double pensionContribution;
};

bool get_marital_status()
{
    std::cout << "Enter Your Marital Status ( 'M' - Married or 'S' - Single ): " ;
    char status ;
    std::cin >> status ;

    if( status == 'M' || status == 'm' ) return true ;
    else if( status == 'S' || status == 's' ) return false ;
    else return get_marital_status() ;
}

template < typename NUMERIC_TYPE >
NUMERIC_TYPE get_number( std::string prompt, int min_value, int max_value )
{
    std::cout << prompt << " [" << min_value << '-' << max_value << "]: " ;
    NUMERIC_TYPE value ;

    if( std::cin >> value && value >= min_value && value <= max_value ) return value ;

    std::cout << "invalid input. please try again\n" ;
    std::cin.clear() ;
    std::cin.ignore( 1000, '\n' ) ;
    return get_number<NUMERIC_TYPE>( prompt, min_value, max_value ) ;
}

int get_num_children()
{
    static const int CHILDREN_AGE_LIMIT = 14;
    static const auto prompt = "Number of Children Under the Age of " + std::to_string(CHILDREN_AGE_LIMIT) +
                               " in Your Household" ;
    return get_number<int>( prompt, 0, CHILDREN_AGE_LIMIT ) ;
}

double get_pension_contrib()
{
    static const double PENSION_CONTRIBUTION_LIMIT = 0.06 ;
    return get_number<double>( "percent pension contribution: ", 0, PENSION_CONTRIBUTION_LIMIT*100 ) / 100 ;
}

TaxDataSet get_tax_data()
{
    const bool married = get_marital_status() ;

    const int n_children = married ? get_num_children() : 0 ;

    double gross_salary = get_number<double>( "enter your gross salary", 0, 1000000 ) ;
    if( married) gross_salary += get_number<double>( "enter your spouse's gross salary", 0, 1000000 ) ;

    return { married, n_children, gross_salary, get_pension_contrib() } ;
}

int main()
{
    const TaxDataSet tax_data = get_tax_data() ;

    // ...
}
Thank you both for the insight!
Topic archived. No new replies allowed.