Question

I'm making a function that takes in 3 unsigned long longs, and applies the law of cosines to find out if the triangle is obtuse, acute or a right triangle. Should I just cast the variables to doubles before I use them?

void triar( unsigned long long& r, 
            unsigned long long x, 
            unsigned long long y, 
            unsigned long long z ) 
{
  if(x==0 || y==0 || z==0) die("invalid triangle sides");

  double t=(x*x + y*y -z*z)/(2*x*y);

  t=acos (t) * (180.0 / 3.14159265);

  if(t > 90) {
    cout<<"Obtuse Triangle"<<endl;
    r=t;

  } else if(t < 90){
    cout<<"Acute Triangle"<<endl;
    r=t;

  } else if(t == 90){
    cout<<"Right Traingle"<<endl;
    r=t;

  }
}
Était-ce utile?

La solution

There is generally no reason why you could not cast if you need floating point arithmetics. However, there is also an implicit conversion from unsigned long to double, so you can also often do completely without casting.

In many cases, including yours, you can cast only one of the arguments to force double arithmetics on a particular operation only. For example,

double t = (double)(x*x + y*y - z*z) / (2*x*y)

This way, all operations except for the division are computed in integer arithmetics and are therefore slighly faster. The cast is still necessary to avoid truncation during division.

Your code contains a comparison of floating point arguments. Floating point arithmetics however almost inevitably reduces accuracy. Avoid limited accuracy, or analyze and control accuracy.

  • Prefer an integer only solution as described in an excellent sister answer if you have a wide enough integral type at your disposal

  • Always avoid conversion from radians to degrees except for presentation to humans

  • Take the value of π from your mathematical library header files (unfortunately, this is platform dependent - try _USE_MATH_DEFINES + M_PI or, if already using boost libraries, boost::math::constants::pi<double>()), or express it analytically. For example, std::atan(1)*2 is the right angle.

  • If you choose double precision, and the ultimate difference value is less than, say, std::numeric_limits<double>::min() * 8, you can probably not tell anything about the triangle and the classification you return is basically bogus. (I made up the value of 8, you will possibly lose way more bits than three.)

Autres conseils

You have a problem with obtuse triangles, x*x + y*y - z*z would mathematically give a negative result, that is then reduced modulo 2^WIDTH (where WIDTH is the number of value bits in unsigned long long, at least 64 and probably exactly that) yielding a - probably large - positive value (or in rare cases 0). Then the computed result of t = (x*x + y*y - z*z)/(2*x*y) can be larger than 1, and acos(t) would return a NaN.

The correct way to find out whether the triangle is obtuse/acute/right-angled with the given argument type is to check whether x*x + y*y < /* > / == */ z*z - if you can be sure the mathematical results don't exceed the unsigned long long range.

If you can't be sure of that, you can either convert the variables to double before the computation,

double xd = x, yd = y, zd = z;
double t = (xd*xd + yd*yd - zd*zd)/(2*xd*yd);

with possible loss of precision and incorrect results for nearly right-angled triangles (e.g. for the slightly obtuse triangle x = 2^29, y = 2^56-1, z = 2^56+2, both y and z would be converted to 2^56 with standard 64-bit doubles, xd*xd + yd*yd = 2^58 + 2^112 would be evaluated to 2^112, subtracting zd*zd then results in 0).

Or you can compare x*x + y*y to z*z - or x*x to z*z - y*y - using only integer arithmetic. If x*x is representable as an unsigned long long (I assume that 0 < x <= y <= z), it's relatively easy, first check whether (z - y)*(z + y) would exceed ULLONG_MAX, if yes, the triangle is obtuse, otherwise calculate and compare. If x*x is not representable, it becomes complicated, I think the easiest way (except for using a big integer library, of course) would be to compute the high and if necessary low 64 (or whatever width unsigned long long has) bits separately by splitting the numbers at half the width and compare those.

Further note: Your value for π, 3.14159265 is too inaccurate, right-angled triangles will be reported as obtuse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top