Как правильно кодировать 5 поплавков в 4 поплавка с максимальной точностью - PullRequest
0 голосов
/ 09 сентября 2018

Сначала я хотел спросить об этом, но у меня появилось собственное решение, которое прекрасно работает, поэтому я просто опубликую это здесь для справки.

Короче говоря, моя ситуация была такова: у меня было 4 доступных канала, которые уже использовались, и я хотел ограничить другое 5-е значение, используя каждый канал в равной степени (так как эти значения связаны), сохраняя при этом максимально возможную точность.

Смотрите мой ответ для моего решения. Если вы найдете лучший / более быстрый вариант или просто найдете способы оптимизации, я буду признателен за ваш ответ!

1 Ответ

0 голосов
/ 09 сентября 2018

Итак, я начал с идеи разбить 5-й поплавок на четыре равные части (например, требуя одинаковой точности) и смешать их с четырьмя другими значениями таким образом, чтобы общая точность 5-й поплавок будет таким же, как и остальные 4-х поплавков .

Итак, я установил это уравнение, которое оказалось очень полезным ( начните здесь, если у вас другие требования, чем у меня! ):

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

с 256 ^ 3 - максимальное значение с плавающей запятой, при котором целые числа по-прежнему можно безопасно различить,

x = максимальное значение каждого из 4-х векторных чисел с плавающей запятой,

y = максимальное значение 5-го числа с плавающей запятой,

px = общая точность вектора с плавающей точкой,

py = точность каждой из 4 частей 5-го числа с плавающей запятой,

и, наконец, px = py ^ 4, чтобы установить условие, что все четыре части, объединенные для получения 5-го числа, должны иметь ту же точность, что и все остальные элементы. Здесь вы должны иметь возможность добавить фактор, чтобы 5-й поплавок получал более или менее точную точность, чем остальные 4, до вас.


Это разрешается до:

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

В моем случае у и х были равны 1, поэтому:

py + py^5 <= 256^3

поместите это в Wolfram Alpha или вычислите приблизительное значение следующим образом:

py = 256^(3/5)

и мы получаем:

py = 27.86 ~= 27
px = 27^4

Проверьте это, и вы увидите:

27 + 27 ^ 5 = 14348934 <256 ^ 3 = 16777216 </p>


Так что если ваши диапазоны равны 0-1 для всех 5 поплавков и вы хотите дать всем 5 поплавкам равную точность , то 27 является магическим числом точности (проверено, что, увидеть ниже). Фактическая точность для всех задействованных поплавков равна 1 части в 27 ^ 4 = 531441, что достаточно.

После того, как вы получили ваше точное значение, используйте следующий код, написанный на C # для Unity (требуется настройка для x / y! = 1 или 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;
}


ПРИМЕЧАНИЕ: Поскольку это не очевидно, все сложение / вычитание 1 в коде позволяет правильно кодировать и декодировать значения 1 и 0 во всех задействованных числах с плавающей запятой.


Я получаю следующие результаты ошибки, показывающие, что точность действительно равна 1/27 ^ 4 = 1.88168E-06:

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

Доказать, что 27 - самое приятное, вот значения для 26:

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

и, наконец, для 28 (что приводит к переполнению для некоторых значений около 1):

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