Search code examples
c++geometrydoubletrigonometryunsigned-long-long-int

Unsigned long long arithmetic into double


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;

  }
}

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.)