Итак, я начал с идеи разбить 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