Should you normalize RGB values by 255 or 256?
61 points by pplanu 3 hours ago | 19 comments

Sesse__ 2 minutes ago
You should multiply by 255.0, optionally add a dither (triangular is okay), and then let the FPU round using its default IEEE 754 round-to-nearest-ties-to-nearest-even mode. None of this crazy 0.5 stuff. :-)
reply
herf 52 minutes ago
I'll argue for the +0.5 solution. First, I don't like half-sized intervals at the edges, and second, a 255-based representation is typically a SDR (not HDR) image.

RGB values represent luminances against some adapted state, and a "zero" in a daylit scene is not "zero luminance" - it's just about 0.001x as bright as the brightest point - it's millions of photons, way more than zero. In a sense our eyes experience contrast on a sliding scale, and there is no absolute zero in the system. For example, broadcast systems historically used 16-235 as their luminance range for SDR. I think any argument that says "we must have zero" is going to have a bias, but I don't think zero is needed for most things.

reply
amavect 6 minutes ago
I agree. Additionally, both 0.0 and 1.0 don't really exist for dithered signals, so a byte should map to [0.5, 255.5] before division by 256. This also solves the signed integer asymmetry, as a signed byte maps to [-127.5, 127.5] before division by 128. I wonder if audio DSP folks have done this already.
reply
yxhuvud 42 minutes ago
Both solutions add 0.5, the difference is where in the process it happens.
reply
dudu24 60 minutes ago
If you have a ruler and it goes to 12 inches, you should normalize by the length L and not by 13, the number of points on the ruler.
reply
lacedeconstruct 46 minutes ago
yes but >> 8 is so much faster
reply
dist-epoch 15 minutes ago
Only in micro-benchmarks.

For real usage, today's CPUs are limited by memory bandwidth.

reply
lacedeconstruct 5 minutes ago
What are you talking about in a hot loop in my software renderer this is like 10x faster

    // color4_t result = {
    //     .r = (src.r * src.a + dst.r * inv_alpha) * INV_255,
    //     .g = (src.g * src.a + dst.g * inv_alpha) * INV_255,
    //     .b = (src.b * src.a + dst.b * inv_alpha) * INV_255,
    //     .a = src.a + (dst.a * inv_alpha) * INV_255
    // };

    // 1/256 but much faster
    color4_t result = {
        .r = (src.r * src.a + dst.r * inv_alpha) >> 8,
        .g = (src.g * src.a + dst.g * inv_alpha) >> 8,
        .b = (src.b * src.a + dst.b * inv_alpha) >> 8,
        .a = src.a + ((dst.a * inv_alpha) >> 8)
    };
reply
dist-epoch 5 minutes ago
Because you are working in the cache.

Also, you should use SIMD.

reply
szundi 13 minutes ago
[dead]
reply
groundzeros2015 45 minutes ago
I’m dumb. Doesn’t 0 start at the beginning?
reply
theyeenzbeanz 51 minutes ago
Should always be 0-255 as that fits an unsigned byte.
reply
crazygringo 30 minutes ago
That's not what the article is about.
reply
Retr0id 33 minutes ago
> assume that in both cases the output values are clamped before the final typecast
reply
Retr0id 37 minutes ago
Both of these assume a linear transfer function, which is rarely the case.
reply
crazygringo 35 minutes ago
Advice for anyone on mobile: read in landscape mode if you want to be able to see the division by 256 version code example at the start.

The HTML/CSS is bad that lets it completely overflow the right edge of the page instead of wrapping.

I re-read this post three times in total confusion before I figured out the most important piece was off-screen entirely.

reply
dist-epoch 19 minutes ago
A similar issue exists in the audio world, for example 16-bit integer audio is between [-32768, 32767] (non-symmetric), but floating point audio is [-1.0, 1.0].
reply
adzm 12 minutes ago
note that floating point audio very often exceeds [-1.0, 1.0] within the pipeline, just to be tamed at the very end of the mix to fit within those bounds. this is pretty much why every modern DAW uses floating point these days.
reply
DigitallyFidget 2 hours ago
255 gives 0-255, which gives you a zero value. 256 is 1-256, you lose the option of setting 0.
reply
crazygringo 29 minutes ago
That's not what the article is about.
reply