<p>Colors shown on your display or saved to standard image file formats use 8 bits per component. So to store these colors it suffices to use four bytes (<code>unsigned char</code>). These colors are usually using <a href="https://en.wikipedia.org/wiki/SRGB">sRGB</a> color space, which includes a non-linear "gamma" transformation that redistributes precision to make it somewhat more perceptually uniform, so it's actually pretty close to the limit of how finely humans can perceive color differences already. (However, the sRGB gamut is only a subset of all the colors that are physically perceivable. Wider gamuts need more bits.)</p>

<p>However, if you're generating or processing colors in graphics software (and not just loading/storing them), there are various reasons you might want to use more precision.</p>

<ul>
<li>Performing operations on colors, such as adjusting brightness and contrast, alpha blending, gamma correction, etc all tend to lose precision. You might want to store colors as 16-bit or more internally to give yourself more precision headroom. Otherwise, repeated rounding off to 8 bits after each operation can cause a progressive loss of quality.</li>
<li>Similarly, when working with linear colors (not the sRGB gamma-encoded colors) for lighting math, extra precision is needed to ensure you have sufficient precision when you eventually convert back to sRGB.</li>
<li>It may be faster and more convenient to work with floating-point colors instead of integer ones. This is especially true on GPUs, where integer math instructions have only a fraction of the throughput of float instructions. But even on CPUs it's certainly easier and probably faster to convert colors to float, do a bunch of operations, and then convert back to integer, rather than trying to do everything with integer fixed-point math.</li>
<li>If you do <a href="https://en.wikipedia.org/wiki/High-dynamic-range_rendering">HDR</a> rendering at all then you'll need more than 8 bits of precision to handle the larger color gamut and intensity range. The new HDR displays just starting to come out accept images with 10 or 12 bits per component.</li>
</ul>

<p>Using <code>double</code> for colors is massive overkill, but using <code>float</code> for internal representation of colors is common for all the reasons above. When working with GPUs, <code>half</code> (16-bit float) is also common, as they support that format in hardware.</p>

CC BY-SA 3.0
2016-08-28T05:09:13.383Z
Excellent answer, thanks! I just didn't get the last part: using a `half` wouldn't incur in the very same problems that you mentioned before, due to the lack of precision? I though that would be particularly important at the GPU, due to the many color calculations that are frequently done in shaders
AndrewSteer
CC BY-SA 3.0
2016-08-28T05:10:51.280Z
@AndrewSteer A `half` is 16 bits, including 11 effective bits of mantissa precision, so while not as precise as `float`, it's still sufficient for most color operations. (Maybe not _quite_ sufficient if you're outputting to a high-gamut HDR display; I'm not sure.)
Nathan Reed
CC BY-SA 4.0
2020-12-24T09:53:52.817Z
for half see openexr
Adam
CC BY-SA 3.0
2016-08-28T02:49:41.457Z
2016-08-28T02:49:41.457Z
Nathan Reed
<p>While doing some reading on how real-time applications handle color under OpneGL, I noticed that some examples implemented Color as a collection of 4 <code>floats</code>, while others used 4 <code>doubles</code>. I have even seen some vertex-compression examples in the game development field that advocated saving colors as 4 <code>short</code>.</p>

<p>All this got me eager for learning more about this: what is the limit precision that OpenGl (or hardwares) handle for colors? More importantly, though, what are the limits in precision above which color differences become impossible to notice?</p>

<p>I have the impression that learning more carefully about that would help me better think and decide on how to implement a Color class for different applications and scenarios (e.g. making trade-off choices between memory, speed and color-variety).</p>

<p>Thanks for your ideas on this.</p>

<opengl><color><color-science><hardware>
How much precision (half, float, double, etc) is enough for a Color class?
AndrewSteer
# How much precision (half, float, double, etc) is enough for a Color class?

(Asked by: username_0 on 2016-08-27)

While doing some reading on how real-time applications handle color under OpneGL, I noticed that some examples implemented Color as a collection of 4 `floats`, while others used 4 `doubles`. I have even seen some vertex-compression examples in the game development field that advocated saving colors as 4 `short`.

All this got me eager for learning more about this: what is the limit precision that OpenGl (or hardwares) handle for colors? More importantly, though, what are the limits in precision above which color differences become impossible to notice?

I have the impression that learning more carefully about that would help me better think and decide on how to implement a Color class for different applications and scenarios (e.g. making trade-off choices between memory, speed and color-variety).

Thanks for your ideas on this.

**Tags:** opengl, color, color-science, hardware


---

## 1 answers

### Answer by username_1 on 2016-08-28. Score: 7 | Accepted answer

Colors shown on your display or saved to standard image file formats use 8 bits per component. So to store these colors it suffices to use four bytes (`unsigned char`). These colors are usually using [sRGB](https://en.wikipedia.org/wiki/SRGB) color space, which includes a non-linear "gamma" transformation that redistributes precision to make it somewhat more perceptually uniform, so it's actually pretty close to the limit of how finely humans can perceive color differences already. (However, the sRGB gamut is only a subset of all the colors that are physically perceivable. Wider gamuts need more bits.)

However, if you're generating or processing colors in graphics software (and not just loading/storing them), there are various reasons you might want to use more precision.

* Performing operations on colors, such as adjusting brightness and contrast, alpha blending, gamma correction, etc all tend to lose precision. You might want to store colors as 16-bit or more internally to give yourself more precision headroom. Otherwise, repeated rounding off to 8 bits after each operation can cause a progressive loss of quality.
* Similarly, when working with linear colors (not the sRGB gamma-encoded colors) for lighting math, extra precision is needed to ensure you have sufficient precision when you eventually convert back to sRGB.
* It may be faster and more convenient to work with floating-point colors instead of integer ones. This is especially true on GPUs, where integer math instructions have only a fraction of the throughput of float instructions. But even on CPUs it's certainly easier and probably faster to convert colors to float, do a bunch of operations, and then convert back to integer, rather than trying to do everything with integer fixed-point math.
* If you do [HDR](https://en.wikipedia.org/wiki/High-dynamic-range_rendering) rendering at all then you'll need more than 8 bits of precision to handle the larger color gamut and intensity range. The new HDR displays just starting to come out accept images with 10 or 12 bits per component.

Using `double` for colors is massive overkill, but using `float` for internal representation of colors is common for all the reasons above. When working with GPUs, `half` (16-bit float) is also common, as they support that format in hardware.

> **Comments:**
> **username_0**: Excellent answer, thanks! I just didn't get the last part: using a `half` wouldn't incur in the very same problems that you mentioned before, due to the lack of precision? I though that would be particularly important at the GPU, due to the many color calculations that are frequently done in shaders  
> **username_1**: @username_0 A `half` is 16 bits, including 11 effective bits of mantissa precision, so while not as precise as `float`, it's still sufficient for most color operations. (Maybe not _quite_ sufficient if you're outputting to a high-gamut HDR display; I'm not sure.)  
> **username_2**: for half see openexr  

---

