Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing 2 Single Floats in One

I've read several links discussing storing 2 or 3 floats in one float. Here's an example:

Storing two float values in a single float variable

and another:

http://uncommoncode.wordpress.com/2012/11/07/float-packing-in-shaders-encoding-multiple-components-in-one-float/

and yet another:

decode rgb value to single float without bit-shift in glsl

I've seen others but all of them use the same principle. If you want to encode x and y, they multiply y by some factor and then add x to it. Well this makes since on paper, but I don't understand how in the world it can work when stored to a floating value. Floating values only have 7 significant digits. If you add a big number and a small number, the small number is just truncated and lost. The precision only shows the value of the big number.

Since everyone seems to prescribe the same method, I tried it myself and it did exactly what I thought it would do. When I decoded the numbers, the number that wasn't multiplied turned out as 0.0. It was completely lost in the encoded float.

Here's an example of some MaxScript I tried to test it:

cp = 256.0 * 256.0
scaleFac = 16777215

for i = 1 to 20 do (
    for j = 1 to 20 do (
            x = (i as float / 20.01f) as float;
            y = (j as float / 20.01f) as float;
            xScaled = x * scaleFac;
            yScaled = y * scaleFac;

            f = (xScaled + yScaled * cp) as float
            print ("x[" + xScaled as string + "] y[" + yScaled as string + "]" + " e[" + f as string + "]")

            dy = floor(f / cp)
            dx = (f - dy * cp)

            print ("x[" + dx as string + "] y[" + dy as string + "]" + " e[" + f as string + "]")
    )
)

dx is 0.0 everytime. Can anyone shed some light on this? NOTE: It doesn't matter whether I make cp = 128, 256, 512 or whatever. It still gives me the same types of results.

like image 762
JamesHoux Avatar asked Oct 14 '25 03:10

JamesHoux


2 Answers

This method works for storing two integers. You're effectively converting your floating point numbers to large integers by multiplying by scaleFac, which is good, but it would be better to make it explicit with int(). Then you need to make sure of two things: cp is greater than the largest number you're working with (scaleFac), and the square of cp is small enough to fit into a floating point number without truncation (about 7 digits for a single precision float).

like image 181
Mark Ransom Avatar answered Oct 17 '25 21:10

Mark Ransom


Here is a working code in C to pack two floats into one float and unpack them.

You should change scaleFactor and cp parameters as according to your possible value ranges (yourBiggestNumber * scaleFactor < cp). It is a precision battle. Try printing a few results to find good values for your case. The example below allows floats in [0 to 1) range.

#include <math.h>

/* yourBiggestNumber * scaleFactor < cp */
double scaleFactor = 65530.0;
double cp = 256.0 * 256.0;

/* packs given two floats into one float */
float pack_float(float x, float y) {
    int x1 = (int) (x * scaleFactor);
    int y1 = (int) (y * scaleFactor);
    float f = (y1 * cp) + x1;
    return f;
}

/* unpacks given float to two floats */
int unpack_float(float f, float* x, float* y){
  double dy = floor(f / cp);
  double dx = f - (dy * cp);
  *y = (float) (dy / scaleFactor);
  *x = (float) (dx / scaleFactor);
  return 0;
}
like image 38
bekce Avatar answered Oct 17 '25 23:10

bekce



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!