Lossy packing 32 bit integer to 16 bit I am working on the lossy 16 bit representation for 32 bit integers that catches all 32 bit range with precision depending on absolute value.

My idea is to store integer lv = sign(x)*ilog2(x) in first bite, and a tail that approximates the error x - 2^ilog2(x).  In order to do that I divide possible error values in 256 bins and store the index of the bin.

To reconstruction of the integer value, I calculate sign(lv) * (2^lv) + bin_size * tail.

I have initial implementation for that, but my code have several problems.


It is not as clear as I'd like to.
short_int32_t(x) should be equal to -short_int32_t(-x) but it is not.  I am pretty sure that it is possible to achieve it, but I am not sure how to do it not making code more ugly.
Any performance suggestions welcome.


Look at functions void set(int32_t) and int32_t get():

template &lt;typename T&gt;
int32_t signum(T x) {
    return (T(0) &lt; x) - (x &lt; T(0));
}

inline int8_t signum_log2(int32_t x) {
    int8_t log2 = static_cast&lt;int8_t&gt;(8 * sizeof(int32_t) - __builtin_clz(abs(x)) - 1);
    static_assert(8 * sizeof(int32_t) == __builtin_clz(1) + 1, "log is wrong");
    return log2 * signum(x);
}

inline int8_t abs_log2(int32_t x) {
    static_assert(8 * sizeof(int32_t) == __builtin_clz(1) + 1, "log is wrong");
    return static_cast&lt;int8_t&gt;(8 * sizeof(int32_t) - __builtin_clz(abs(x)) - 1);
}


class short_int32_t {
public:
    short_int32_t(int32_t x) {
        set(x);
    }

    void operator=(int32_t x) {
        set(x);
    }

    bool operator&lt;(short_int32_t x) {
        return m_log == x.m_log ? (signum(m_log) * m_tail &lt; signum(x.m_log) * x.m_tail) : (m_log &lt; x.m_log);
    }

    short_int32_t operator*(short_int32_t x) {
        return short_int32_t(get() * x.get());
    }

    short_int32_t operator+(short_int32_t x) {
        return short_int32_t(get() + x.get());
    }

    short_int32_t operator-(short_int32_t x) {
        return short_int32_t(get() - x.get());
    }

    short_int32_t operator*(int32_t x) {
        return short_int32_t(get() * x);
    }

    short_int32_t operator+(int32_t x) {
        return short_int32_t(get() + x);
    }

    short_int32_t operator-(int32_t x) {
        return short_int32_t(get() - x);
    }


    void set(int32_t x) {
        if (x == 0) {
            m_log = 0;
            m_tail = 0;
            return;
        }

        int8_t abs_log = abs_log2(x);
        int32_t interval = abs_log ? 1 &lt;&lt; abs_log : 0;
        if (interval == abs(x)) {
            m_log = x &lt; 0 ? -abs_log : abs_log;
            m_tail = 0;
            return;
        }

        int32_t bin_size = interval &lt; 512 ? 1 : (x &lt; 0 ? (interval &gt;&gt; 9) : (interval &gt;&gt; 8));

        m_log = x &gt; 0 ? abs_log : -abs_log - 1;
        int32_t tail = x &gt; 0 ? x - interval : ((1 &lt;&lt; -m_log) + x);

        m_tail = tail / bin_size;
    }

    int32_t get() const {
        int32_t interval = 1 &lt;&lt; abs(m_log);
        int32_t bin_size = interval &lt; 512 ? 1 : (m_log &lt; 0 ? (interval &gt;&gt; 9) : (interval &gt;&gt; 8));

        return signum(m_log) * interval + static_cast&lt;int32_t&gt;(m_tail) * bin_size;
    }

public:
    int8_t m_log;
    uint8_t m_tail;
};

short_int32_t operator*(int32_t x, short_int32_t y) {
    return y * x;
}

short_int32_t operator+(int32_t x, short_int32_t y) {
    return y + x;
}

short_int32_t operator-(int32_t x, short_int32_t y) {
    return short_int32_t(x - y.get());
}
A few improvements:


Assignment operator (=) should return a reference to short_int32_t, returning *this in the implementation.
Operator &lt; or any other comparison/arithmetical operator for that matter, should be const, since they only read the object's state. E.g.: bool operator &lt; (short_int32_t x) const.
Where are the other comparison operators, BTW? I would expect at least ==,!=,&lt;,&gt;,&lt;=,&gt;=.
get/set are good enough names, but wouldn't maybe set32 and get32 be more idiomatic in this case? Or perhaps even more explicit, such as fromInt32 and toInt32.
Consider making those global inline functions member of the class, if they are never used elsewhere.