<p>This will not work. The principle you are attempting to use would work (with some adjustment) if you were converting between a 32-bit unsigned <em>integer</em> representation and four 8-bit integers, because there are exactly enough bits to use. But the 32-bit “single precision” floating point format uses some of its bits to represent the exponent and the sign:</p>
<p><a href="https://i.sstatic.net/A1m0P.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/A1m0P.png" alt="Diagram of 32-bit single precision floating point format" /></a></p>
<p><em>(<a href="https://en.wikipedia.org/wiki/File:Float_example.PNG" rel="nofollow noreferrer">Image</a> by Wikipedia user Fresheneesz)</em></p>
<p>There are only 23 bits available (effectively 24) to represent an integer value. So, you cannot simply pack four 8-bit integers into an mathematically-integer value stored in a 32-bit float — you will run out of precision, and the component stored in the low bits will be corrupted.</p>
<p>However, it <em>is</em> possible to pack the <em>amount</em> of data you want into a float; the key is that you have to <em>reinterpret the bits of the float</em> as a 32-bit integer or vice versa. In GLSL, this is provided by the functions <code>floatBitsToUint()</code> and <code>uintBitsToFloat()</code>. Once you have the unsigned integer, you can proceed as you intended — <strong>but the produced <code>float</code> will not be in the range 0 to 1.</strong></p>

CC BY-SA 4.0
2023-02-06T07:02:47.237Z
Hey, thanks a lot for the answer!  I realized that I'd need $\text{split}(x)_i:=\lfloor(256(256^ix-\lfloor256^ix\rfloor)\rfloor/255$, but regarding your argument about $32$ bit floats, the specification says that `precision highp float;` just guarantees at least $32$ bit precision, but it could be higher, so do you think my idea could make more sense in this regard?  If the precision is only $32$, I won't get $256^4$ bins, but it also wouldn't lead to any large errors, I think?
fweth
CC BY-SA 4.0
2023-02-06T16:09:00.190Z
@fweth What you care about for this purpose is the number of _mantissa_ bits, not the total number of bits. 32-bit floats have 24 (effective) mantissa bits, and 64-bit floats have 53 mantissa bits. As I already described, 24 bits means you lose 1 of your 4 components. 53 bits is sufficient for everything, but from what I've heard, even if a GPU supports 64-bit floats, they're so slow as to be unwise to use. (I don't know whether `highp` ever maps to 64-bit.)
Kevin Reid
CC BY-SA 4.0
2023-02-05T17:20:08.243Z
2023-02-05T17:20:08.243Z
Kevin Reid
<p>I know, this is a silly question, but since I need this so often, I just want to double check that I made no mistake.</p>
<p>Working with most shader languages, a texture can store <span class="math-container">$8$</span> bits per channels, which in the shader programm appear as value in the range range <span class="math-container">$r_8:=\{0, 1/255, 2/255,\dots,1\}$</span>.  What I want to do is to transform between a single <span class="math-container">$32$</span> bit floating point value in <span class="math-container">$(0, 1)$</span> (I don't care about the floating point specifics here and just think of it as real number) and a <span class="math-container">$4$</span>-tuple of values in <span class="math-container">$r_8$</span>, so <span class="math-container">$\text{split}:(0,1)\to r_8^4$</span> and <span class="math-container">$\text{comb}:r_8^4\to(0,1)$</span>.</p>
<p>I specifically want that <span class="math-container">$\text{comb}(\text{split}(x))=\text{round}(256^4 x)/256^4$</span> for almost all <span class="math-container">$x$</span> (I don't care about the ones lying on boundaries between bins).</p>
<p>My implementation is the following:</p>
<ul>
<li><span class="math-container">$\text{split}(x)_i := \text{floor}(256^ix)/(255\cdot 256^{i-1})$</span></li>
<li><span class="math-container">$\text{comb}(x_0,\dots,x_3):=255(x_0/256+\dots+x_3/256^4)+256^4/2$</span></li>
</ul>
<p>Is this correct or did I overlook something?</p>
<h2>Edit</h2>
<p>I realized that the first part should be <span class="math-container">$$\text{split}(x)_i:=\lfloor(256(256^ix-\lfloor256^ix\rfloor)\rfloor/255$$</span></p>

<glsl><mathematics><float>
Splitting Floats To Texture Channels
fweth
# Splitting Floats To Texture Channels

(Asked by: username_0 on 2023-02-05)

I know, this is a silly question, but since I need this so often, I just want to double check that I made no mistake.

Working with most shader languages, a texture can store $8$ bits per channels, which in the shader programm appear as value in the range range $r\_8:=\{0, 1/255, 2/255,\dots,1\}$. What I want to do is to transform between a single $32$ bit floating point value in $(0, 1)$ (I don't care about the floating point specifics here and just think of it as real number) and a $4$-tuple of values in $r\_8$, so $\text{split}:(0,1)\to r\_8^4$ and $\text{comb}:r\_8^4\to(0,1)$.

I specifically want that $\text{comb}(\text{split}(x))=\text{round}(256^4 x)/256^4$ for almost all $x$ (I don't care about the ones lying on boundaries between bins).

My implementation is the following:

* $\text{split}(x)\_i := \text{floor}(256^ix)/(255\cdot 256^{i-1})$
* $\text{comb}(x\_0,\dots,x\_3):=255(x\_0/256+\dots+x\_3/256^4)+256^4/2$

Is this correct or did I overlook something?

## Edit

I realized that the first part should be $$\text{split}(x)\_i:=\lfloor(256(256^ix-\lfloor256^ix\rfloor)\rfloor/255$$

**Tags:** glsl, mathematics, float


---

## 1 answers

### Answer by username_1 on 2023-02-05. Score: 1 | Accepted answer

This will not work. The principle you are attempting to use would work (with some adjustment) if you were converting between a 32-bit unsigned *integer* representation and four 8-bit integers, because there are exactly enough bits to use. But the 32-bit “single precision” floating point format uses some of its bits to represent the exponent and the sign:

[![Diagram of 32-bit single precision floating point format](https://i.sstatic.net/A1m0P.png)](https://i.sstatic.net/A1m0P.png)

*([Image](https://en.wikipedia.org/wiki/File:Float_example.PNG) by Wikipedia user Fresheneesz)*

There are only 23 bits available (effectively 24) to represent an integer value. So, you cannot simply pack four 8-bit integers into an mathematically-integer value stored in a 32-bit float — you will run out of precision, and the component stored in the low bits will be corrupted.

However, it *is* possible to pack the *amount* of data you want into a float; the key is that you have to *reinterpret the bits of the float* as a 32-bit integer or vice versa. In GLSL, this is provided by the functions `floatBitsToUint()` and `uintBitsToFloat()`. Once you have the unsigned integer, you can proceed as you intended — **but the produced `float` will not be in the range 0 to 1.**

> **Comments:**
> **username_0**: Hey, thanks a lot for the answer!  I realized that I'd need $\text{split}(x)_i:=\lfloor(256(256^ix-\lfloor256^ix\rfloor)\rfloor/255$, but regarding your argument about $32$ bit floats, the specification says that `precision highp float;` just guarantees at least $32$ bit precision, but it could be higher, so do you think my idea could make more sense in this regard?  If the precision is only $32$, I won't get $256^4$ bins, but it also wouldn't lead to any large errors, I think?  
> **username_1**: @username_0 What you care about for this purpose is the number of _mantissa_ bits, not the total number of bits. 32-bit floats have 24 (effective) mantissa bits, and 64-bit floats have 53 mantissa bits. As I already described, 24 bits means you lose 1 of your 4 components. 53 bits is sufficient for everything, but from what I've heard, even if a GPU supports 64-bit floats, they're so slow as to be unwise to use. (I don't know whether `highp` ever maps to 64-bit.)  

---

