简体   繁体   中英

How to encode 5 floats into 4 floats equally with maximum precision

I was initially going to ask this, but I turned out with my own solution that works great, so I'll just post this here for reference.

In short, my situation was, I had 4 channels available, which were already used, and I wanted to cramp in another 5th value, using each channel equally (as these values are related), while retaining the highest precision possible.

See my answer for my solution. If you find a better / faster one or simply find optimizations to do, I'd appreciate your answer!

So I started off with the idea to split the 5th float in four equal parts (as in, requiring the same precision) and mix them into the four other values in such a way, that the total precision of the 5th float would be the same as the other 4 floats .

So I set up this equation which turned out to be of great help ( start here if you have different requirements than me! ):

y * py + x * px * py <= 256^3

with 256^3 being the maximum float value at which integers are still safely distinguishable,

x = maximum value of each of the 4 vector floats,

y = maximum value of the 5th float,

px = total precision of the vector floats,

py = precision of each of the 4 parts of the 5th float,

and finally px = py^4 to set the condition that all four parts combined to get the 5th float should have the same precision as all other floats. Here you should be able to add a factor to make the 5th float get more or less precision that the other 4, up to you.


This resolves to:

y * py + x * py^4 * py <= 256^3

In my case y and x were both 1, so:

py + py^5 <= 256^3

put that in Wolfram Alpha or calculate the approximate as follows:

py = 256^(3/5)

and we get:

py = 27.86 ~= 27
px = 27^4

Test that and you'll see:

27 + 27^5 = 14348934 < 256^3 = 16777216


So in case your ranges are 0-1 for all 5 floats and you want to give all 5 floats equal precision , then 27 is the magic precision number (verified that, see below). The actual precision for ALL involved floats is then 1 part in 27^4 = 531441, which is plenty.

Once you got your precision number, use the following code, written in C# for Unity (Needs adjustments for x/y != 1 or px != py^4):

public Vector4 Encode(Vector4 x, float y)
{
    float p = 27; // So that p^5+p < 256^3; approx: 256^(3/5) = 27.85 => 27; Actual precision is 1/(27^4) = 1/531441
    float p0 = 1, p1 = p, p2 = p * p, p3 = p * p * p, p4 = p * p * p * p;

    // Split in 4 y-floats
    float yC = y * (p4 - 1);
    float yC1 = Mathf.Floor(yC % p4 / p3);
    float yC2 = Mathf.Floor(yC % p3 / p2);
    float yC3 = Mathf.Floor(yC % p2 / p1);
    float yC4 = Mathf.Floor(yC % p1 / p0);

    // Test by combining each z-float again
    //float yTest = (yC1 * p3 + yC2 * p2 + yC3 * p1 + yC4 * p0) / (p4 - 1);

    // Encode each y-float into x as xC
    Vector4 xC = new Vector4(
            Mathf.Floor(x.x * p4) * p1 + yC1,
            Mathf.Floor(x.y * p4) * p1 + yC2,
            Mathf.Floor(x.z * p4) * p1 + yC3,
            Mathf.Floor(x.w * p4) * p1 + yC4);

    return xC;
}

public Vector4 Decode(Vector4 xC, out float yDC)
{
    float p = 27; // So that p^5+p < 256^3; approx: 256^(3/5) = 27.85 => 27
    float p0 = 1, p1 = p, p2 = p * p, p3 = p * p * p, p4 = p * p * p * p;

    // Decode xC to get each y-float
    float yDC1 = xC.x % p1;
    float yDC2 = xC.y % p1;
    float yDC3 = xC.z % p1;
    float yDC4 = xC.w % p1;

    // Combine y-floats to get decoded y as yDC
    yDC = (yDC1 * p3 + yDC2 * p2 + yDC3 * p1 + yDC4 * p0) / (p4 - 1);

    // Get original x from xC as xDC
    Vector4 xDC = new Vector4(
            Mathf.Floor(xC.x / p1) / p4,
            Mathf.Floor(xC.y / p1) / p4,
            Mathf.Floor(xC.z / p1) / p4,
            Mathf.Floor(xC.w / p1) / p4);

    return xDC;
}


NOTE: As it is not obvious, the whole add / subtract 1 in the code allows the values of 1 and 0 in all involved floats to be coded and decoded properly.


I get the following error results, showing the precision is indeed 1/27^4 = 1.88168E-06:

X Vector: Avg9.200859E-07 | Max1.881272E-06  Y Float : Avg9.223418E-07 | Max1.881272E-06

To prove 27 is the sweet spot, here are values for 26:

X Vector: Avg1.072274E-06 | Max2.190471E-06  Y Float : Avg1.07382E-06  | Max2.190471E-06

and finally for 28 (resulting in an overflow for certain values near 1):

X Vector: Avg7.914192E-07 | Max1.66893E-06  Y Float : Avg0.001480901 | Max0.9987262

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM