#include #include // FIND THE BUGS // // Suppose a point has coordinates (x, y). We want to // determine the angle, measured counterclockwise from the // positive x-axis, of the ray from the origin through that // point. // // The angle must be printed as the closest integer number // of degrees from 0 through 359. // // Calculating this angle using std::atan(...) requires // care because the quotient y/x does not uniquely identify // the quadrant containing the point. // // For example, the angle of the point // // (4, 4) // // is 45 degrees, and // // atan(4/4) = atan(1) // // gives the expected angle. // // However, the angle of // // (-4, -4) // // is 225 degrees, while // // atan((-4)/(-4)) = atan(1) // // again gives 45 degrees. // // Thus, the signs of 'x' and 'y' must also be considered // when determining the angle. // // There are two additional issues: // // 1. std::atan(...) returns an angle in radians rather // than degrees. // // 2. The final answer must be rounded to the closest // integer degree and must be between 0 and 359. // // The following program attempts to perform this // calculation, but it contains MANY bugs. // // Find and correct all of them. // // It may be useful to keep a list of test points for which // you know the correct angles and fix one bug at a time. // // Be sure to fix spelling, formatting and other aesthetic // errors as well as errors that cause incorrect results. // // A list of test cases and, later, a list of the errors // appear below. Do not scroll down to the list of errors // until you have spent a reasonable amount of time trying // to find the problems yourself. // // You may need to consult a reference for std::atan(...). // // Once you have corrected the program, investigate // // std::atan2( y, x ). // // How much of your corrected code could be replaced by // using this function? // Function declarations int main(); // Function definitions int main() { while ( true ) { double x{}; std::cout << "Enter an x value: "; std::cin >> x; double y{}; std::cout << "ENter a y value: "; std::cin >> y; double result{}; int degrees{}; if ( y >= 0.0 ) { // We are in the first or second quadrants, // so use the fact that // // rise // tan(theta) = ----. // run result = std::atan( y/x ); } else { result = M_PI + std::atan( y/x ); } // The std::atan function returns a value in // [0, M_PI), so the above calculation returns // a value in [0, 2*M_PI). // // There are 360 degrees in a circle, so convert // the result from radians to degrees. degrees = 360/M_PI*result; std::cout << "The angle of (" << x << ", " << y << " ) is " << degrees << " degrees" << std::endl << std::endl; } return 0; } /****************************************************************** * A LIST OF TEST CASES * * Before looking at the list of errors, test points in all four * quadrants as well as points on all four coordinate axes. * * x y angle * * 1 0 0 * 1 1 45 * 0 1 90 * -1 1 135 * -1 0 180 * -1 -1 225 * 0 -1 270 * 1 -1 315 * * You should also test points close to the positive x-axis, * where rounding may cause the result to cross between * 359 degrees and 0 degrees: * * x y angle * * 1000 -9 359 * 1000 -8 0 * 1000 8 0 * 1000 9 1 * * What should happen for the point * * (0, 0)? * * There is no direction from the origin to itself, so its angle * is undefined. A complete solution should deal with this case * separately. ******************************************************************/ /****************************************************************** * A LIST OF ERRORS * * Here is a list of the bugs. Do not review these until you have * made a reasonable effort to find and correct the problems * yourself. * * 1. "ENter" should be "Enter". * * 2. There is an unwanted space before the closing parenthesis * when the coordinates are printed: * * (x, y ) * * should be * * (x, y). * * 3. std::atan(...) returns an angle in the interval * * (-pi/2, pi/2), * * not [0, pi). * * 4. The quadrant correction is incorrect. In particular, the * sign of 'x' is essential when determining whether pi must * be added to the result. * * 5. If x == 0, calculating y/x is unnecessary and should be * avoided. The points * * (0, y), y > 0 * * have angle pi/2, while * * (0, y), y < 0 * * have angle 3*pi/2. * * 6. The point (0, 0) has no defined angle and should be handled * separately. * * 7. To convert radians to degrees, multiply by * * 180/pi, * * not 360/pi. * * 8. Assigning a floating-point value directly to an 'int' * truncates the value; it does not round to the closest * integer. Use std::round(...) before converting the result * to an integer. * * 9. After rounding, an angle close to 2*pi may become * * 360 degrees. * * Since the required range is 0 through 359 degrees, * * 360 * * must be changed to * * 0. * ******************************************************************/ /****************************************************************** * ONE CORRECTED VERSION USING std::atan ******************************************************************/ /* int main() { while ( true ) { double x{}; std::cout << "Enter an x value: "; std::cin >> x; double y{}; std::cout << "Enter a y value: "; std::cin >> y; if ( (x == 0.0) && (y == 0.0) ) { std::cout << "The angle of (0, 0) is undefined." << std::endl << std::endl; continue; } double result{}; if ( x > 0.0 ) { result = std::atan( y/x ); if ( result < 0.0 ) { result += 2.0*M_PI; } } else if ( x < 0.0 ) { result = M_PI + std::atan( y/x ); } else if ( y > 0.0 ) { result = M_PI/2.0; } else { result = 3.0*M_PI/2.0; } int degrees{ static_cast( std::round( 180.0/M_PI*result ) ) }; if ( degrees == 360 ) { degrees = 0; } std::cout << "The angle of (" << x << ", " << y << ") is " << degrees << " degrees" << std::endl << std::endl; } return 0; } */ /****************************************************************** * USING std::atan2 * * std::atan2(y, x) performs the quadrant analysis for us. * * It returns an angle in radians in the interval [-pi, pi]. * Consequently, the result may still be negative and must be * converted to the required range from 0 through 359 degrees. * * After separately handling the point (0, 0), the calculation * can be reduced to: * * int degrees{ * static_cast( * std::round( 180.0/M_PI*std::atan2( y, x ) ) * ) * }; * * if ( degrees < 0 ) { * degrees += 360; * } * * if ( degrees == 360 ) { * degrees = 0; * } * * Thus, std::atan2(...) eliminates all of the quadrant logic, * although conversion, rounding and normalization are still * required. ******************************************************************/