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\$ Commented 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\$ Commented 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
    Commented 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\$ Commented Nov 30, 2016 at 2:49

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.