2

I have to take the coordinates of the vertices of a triangle from the user and tell if it is a right-angled triangle or not. I'm using Pythagoras Theorem to Find out i.e. h * h = b * b + p * p

But surprisingly this doesn't work for some specific right-angled triangles. Here is one such Triangle:

Vertex A: (x, y) = (1, 3)

Vertex B: (x, y) = (1, 1)

Vertex C: (x, y) = (5, 1)

It calculates perfectly, which I figured out by printing the calculation, but still doesn't work.

Then I tried by using sqrt() function from the cmath library this way: h = sqrt(b * b + p * p)

Logically it is the same, but it worked.

I want to understand, why the earlier method is not working?

Here is a simplified version of My Code:

#include <iostream>
#include <cmath>

using namespace std;

class Vertex {

    double x, y;

public:
    void take_input(char obj) {
        cout << endl << "   Taking Coordinates of Vertex " << obj << ": " << endl;

        cout << "       Enter the x component: ";
        cin >> x;
        cout << "       Enter the y component: ";
        cin >> y;
    }

    double distance(Vertex p) {
        double dist = sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));

        return dist;
    }
};

class Triangle {
    Vertex a, b, c;

public:

    void take_inp(string obj) {
        cout << endl << "Taking Vertices of the Triangle " << obj << ": " << endl;
        cout << "   Verteces should be in a counter clockwise order (as per convention)." << endl;

        a.take_input('A');
        b.take_input('B');
        c.take_input('C');
    }

    void is_rt_ang() {

        double h = a.distance(c)*a.distance(c);
        double bp = a.distance(b)*a.distance(b) + b.distance(c)*b.distance(c);

        /*
            // Strangely this attempt works which is logically the same: 
            double h = a.distance(c);
            double bp = sqrt(a.distance(b)*a.distance(b) + b.distance(c)*b.distance(c));
        */

        if (h == bp) {
            cout << "Angle is 90" << endl;
            cout << h << " = " << bp << endl;
            cout << "It is Right-Angled" << endl;
        }
        else {
            cout << "Angle is not 90!" << endl;
            cout << h << " != " << bp << endl;
            cout << "It is Not a Right-Angled" << endl;
        }
    }
};

int main()
{
    Triangle tri1, tri2;

    tri1.take_inp("tri1");

    tri1.is_rt_ang();

    return 0;
}
DaniyalAhmadSE
  • 807
  • 11
  • 20

2 Answers2

5

The line

double dist = sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));

in the Vertex::distance method gives you an approximation of a square root which is rarely going to coincide with an exact answer. This is because most real numbers can't be represented in floating point arithmetic.

But in given code sample you can make do without sqrt. Replace Vertex::distance method with a method

 double distance_square(Vertex p) {
    double dist_square = (x-p.x)*(x-p.x) + (y-p.y)*(y-p.y);
    return dist_square;
}

and call it like this in Triangle::is_rt_ang:

    double h = a.distance_square(c);
    double bp = a.distance_square(b) + b.distance_square(c);

This solution is still flawed because floating-point multiplication is also a subject to rounding errors. But if it is guaranteed that you are going to work only with integer coordinates, you can replace all doubles in your code with ints and for them there is no problem with multiplication (besides possibly going out of bounds for large numbers).

EDIT: Also a comment on printing

It calculates perfectly, which I figured out by printing the calculation, but still doesn't work.

When you print doubles you need to set precision manually in order to avoid rounding. If in your code I replace a line

cout << h << " != " << bp << endl;

with

cout << std::setprecision(std::numeric_limits<double>::digits10) << std::fixed << h << " != " << bp << endl;

then for example triangle from the question I get the output

Angle is not 90!
20.000000000000004 != 20.000000000000000
It is Not a Right-Angled

For this to compile you will need to add #include <limits> and #include <iomanip>.

Andrey Davydov
  • 131
  • 1
  • 4
  • Thanks for the response, I'd like to know that why are the values not same, even when the coordinates provided are of an actual right-angled triangle? – DaniyalAhmadSE Mar 21 '21 at 16:55
  • 3
    @DaniyalAhmad Values are not the same because two different expressions involving floating-point arithmetic produce slightly different rounding errors. If this effect is new to you, I suggest to check out the link from the answer by Alex Riveron - [Is floating point math broken?](https://stackoverflow.com/q/588004/12390738) There are many good answers and useful links. – Andrey Davydov Mar 21 '21 at 17:21
  • 2
    Side remark: `sqrt(a*a+b*b)` should pretty much always be replaced by `hypot(a,b)`. – njuffa Mar 21 '21 at 19:09
2

In your is_rt_ang function you're assuming that your hypotenuse is always going to be the edge AC, but it doesn't seem like you're doing anything to verify this.

double h = a.distance(c)*a.distance(c);
double bp = a.distance(b)*a.distance(b) + b.distance(c)*b.distance(c);

You could try getting the squares of all your distances first, (AC)^2, (AB)^2, and (BC)^2, then finding the candidate for hypotenuse by taking the max value out of the three, then do something like:

bool isRightTriangle = max == (min1 + min2)

You may also be running into some kind of round-off error with floating point numbers. It is common to use a an epsilon value when comparing floating point numbers because of the inherent round-off errors with them. If you don't need floating point values maybe use an integer, or if you do need floating point values try using an epsilon value in your equalities like:

abs(h - bp) <= epsilon

You should be able to find more information about floating point values, round-off errors, and machine epsilons on the web.

Here is a link to a SO Q/A that talks about floating point math that may be a good resource for you: Is floating point math broken?

Alex Riveron
  • 389
  • 1
  • 10