c++operator-overloadingfixed-point

Is there an efficient way to overload operators for structs when the underlying data is piecewise?


I have various methods for accumulating quantities in fixed-precision numerics based on the following data type:

int95_t {
  long long int x;
  int y;
};

The numbers count forward in x until it may run out of bounds and wrap around, at which point y accumulates +1 for overflow and -1 for underflow. (I also have methods for adding floating point numbers, which may cause several overflows or underflows of x and thus accumulate several times in y).

The methods for adding or subtracting in this data type are somewhat involved.

int95_t sum(const int95_t a, const int95_t b) {
  int95_t result = { a.x + b.x, a.y + b.y };
  result.y += (1 - (2 * (b.x < 0LL))) * ((a.x ^ result.x) < 0 && (a.x ^ b.x) >= 0LL) * 2;
  return result;
}

int95_t subtract(const int95_t a, const int95_t b) {
  const int95_t neg_b = { -b.x, -b.y + (2 * (b.x == LLONG_MIN)) };
  return sum(a, neg_b);
}

I have overloaded the operators +, -, +=, and -= for this data type, but that doesn't help to clean up my code all that much and for the following reason: in many contexts I will be storing not an array of int95_t values but two separate arrays of long long int and int values. (The reason is that there are many conditions under which the supernary accumulator y can be safely ignored, and I want to conserve memory bandwidth.) Thus, I have a lot of situations where, in order to add one int95_t to another in these circumstances, it seems I would still need to do:

long long int primary[8];
int secondary[8];
int95_t add_this = { 81573835283, 3816 };

for (int i = 0; i < 8; i++) {
  int95_t tmp = { primary[i], secondary[i] };
  tmp += add_this;
  primary[i] = tmp.x;
  secondary[i] = tmp.y;
}

Is there any better way? I would hope that I could count on any given C++ compiler to properly interpret something like this in the above case, but I'm not sure:

for (int i = 0; i < 8; i++) {
  { primary[i], secondary[i] } += add_this;
}

Thanks for any help you all can offer.


Solution

  • One of solutions could be to create a temporary object with overloaded arithmetics which holds references to operand components. For example:

    class split
    {
        long long int &rx;
        int &ry;
    
    public:
        split(long long int &x, int &y) : rx { x }, ry { y } {}
    
        split &operator+=(const int95_t &v)
        {
            // implement your += logic here with rx and ry modification
            return *this;
        }
    };
    
    for (int i = 0; i < 8; i++) {
      split(primary[i], secondary[i]) += add_this;
    }
    

    You can also re-use this class for int95_t overloaded operators implementation, i.e.

    int95_t val = { 81573835283, 3816 };
    
    split(val.x, val.y) += add_this;