6
\$\begingroup\$

Rather than checking if my angle is in the range of 0 to 2pi every time it gets set, I got the idea to store it as an unsigned short with 0xFFFF being +2pi, thus the standard overflow behavior for unsigned numbers should keep it bound to the desired range.

Is it a good idea to do it this way, or is there something I'm missing?

#ifndef ANGLE_H
#define ANGLE_H
#include <cstdint>

class Angle
{
//static constexpr long double     _PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348;
  static constexpr long double _TWO_PI = 6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696;

    static inline __attribute((always_inline)) __attribute((pure)) 
    uint16_t uint16_from_double(const double a) { return (a * 0x00010000) / _TWO_PI; } 
    uint16_t _theta;

    Angle(uint16_t theta) :
        _theta(theta)
    {
    }

public:
    Angle(double t = 0) : 
        _theta(uint16_from_double(t));
    {
    }

    inline operator double() const
    {
        return radians();
    }

    inline double radians() const { return ((double) _theta) / 0x00010000) * _TWO_PI; }
    inline double degrees() const { return ((double) _theta) / 0x00010000) * 360; }

//everything is less than and greater than everything else, because it's a circle
//so return if subtracting will get us there faster than adding...
    inline bool operator<(const Angle & it) const
    {
        return _theta < it._theta? (it._theta - _theta) < 0x00008000 : (_theta - it._theta) >= 0x00008000;
    }
    inline bool operator<=(const Angle & it) const
    {
        return _theta < it._theta? (it._theta - _theta) <= 0x00008000 : (_theta - it._theta) > 0x00008000;
    }

    inline bool operator>(const Angle & it) const
    {
        return _theta < it._theta? (it._theta - _theta) > 0x00008000 : (_theta - it._theta) <= 0x00008000;
    }
    inline bool operator>=(const Angle & it) const
    {
        return _theta < it._theta? (it._theta - _theta) >= 0x00008000 : (_theta - it._theta) < 0x00008000;
    }

    inline Angle minDelta(const Angle & it) const
    {
        uint16_t i = _theta < it._theta? it._theta - _theta :  _theta - it._theta;
        return Angle(i < 0x00007FFF? i : 0x00010000 - i);
    }

    inline const Angle & operator=(double a)
    {
        _theta = uint16_from_double(a);
        return *this;
    }

    inline const Angle & operator+=(double a)
    {
        _theta += uint16_from_double(a);
        return *this;
    }

    inline const Angle & operator-=(double a)
    {
        _theta -= uint16_from_double(a);
        return *this;
    }

    inline const Angle & operator*=(double a)
    {
        _theta *= a;
        return *this;
    }

    inline const Angle & operator/=(double a)
    {
        _theta /= a;
        return *this;
    }
};

#endif // ANGLE_H
\$\endgroup\$
1
  • \$\begingroup\$ the openGL functions that used degrees have long since been deprecated and removed. So I would prefer the operator double to return radians. \$\endgroup\$ Nov 28, 2016 at 17:15

1 Answer 1

3
\$\begingroup\$

I find it amusing that you have written _TWO_PI out to 89 digits of precision. The ratio of the circumference of the observable universe relative to the size of a hydrogen atom is just 40 digits, which means that you couldn't possibly need any more precision than that for any physical calculation. In any case, you're quantizing it to a uint16_t, which gives you less precision than a float, so even long double is overkill.

You could use one of these suggestions for the value of π. In any case, you should never use an identifier with a leading underscore — names with one underscore are reserved for standard libraries, two underscores for the compiler.

\$\endgroup\$
3
  • \$\begingroup\$ I once found a game engine that defined pi out to 120 or so digits of precision, I figured it's just a 'let the compiler worry about it' sort of idea, and copied that methodology irrational constants. Is there really a chance of a private identifier colliding with the standard library? I'm pretty sure _[A-Z][a-zA-Z0-9_]+ is what has to be avoided for collisions with macros--meaning using it as a constant should cause a compile error if it collides--and _[a-z][a-zA-Z0-9_]+ is only disallowed in the global scope. \$\endgroup\$ Nov 28, 2016 at 19:50
  • \$\begingroup\$ @PatrickJeeves The standard defined _Bool as a type (at least in C99). It's not inconceivable that they could define _FOO as a macro. Suppose they defined _TWO_PI as a macro that expands to the digits you included, then when the compiler tried to parse your code, it would see static constexpr long double 6.28 ... = 6.28 ... which is not valid, so it would be a compiler error. \$\endgroup\$
    – Justin
    Nov 29, 2016 at 5:33
  • \$\begingroup\$ You are wrong about _names. There are only prohibited names defined by regexps _[A-Z].* (underscore at beginning and first capital letter) and __.* (two underscores at beginning). \$\endgroup\$ Nov 30, 2016 at 2:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.