314

I keep seeing this constant pop up in various graphics header files

0.0039215689

It seems to have something to do with color maybe?

Here is the first hit on Google:

void RDP_G_SETFOGCOLOR(void)
{
    Gfx.FogColor.R = _SHIFTR(w1, 24, 8) * 0.0039215689f;
    Gfx.FogColor.G = _SHIFTR(w1, 16, 8) * 0.0039215689f;
    Gfx.FogColor.B = _SHIFTR(w1, 8, 8) * 0.0039215689f;
    Gfx.FogColor.A = _SHIFTR(w1, 0, 8) * 0.0039215689f;
}

void RDP_G_SETBLENDCOLOR(void)
{
    Gfx.BlendColor.R = _SHIFTR(w1, 24, 8) * 0.0039215689f;
    Gfx.BlendColor.G = _SHIFTR(w1, 16, 8) * 0.0039215689f;
    Gfx.BlendColor.B = _SHIFTR(w1, 8, 8) * 0.0039215689f;
    Gfx.BlendColor.A = _SHIFTR(w1, 0, 8) * 0.0039215689f;

    if(OpenGL.Ext_FragmentProgram && (System.Options & BRDP_COMBINER)) {
        glProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, Gfx.BlendColor.R, Gfx.BlendColor.G, Gfx.BlendColor.B, Gfx.BlendColor.A);
    }
}

//...more like this

What does this number represent? Why does no one seem to declare it as a const?

I couldn't find anything on Google that explained it.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
crush
  • 16,713
  • 9
  • 59
  • 100
  • Note: Color components (e.g. red, green, blue, alpha) are often represented as an integer in [0 ; 255] in many APIs – xav Mar 24 '14 at 21:49
  • 16
    Is there any reason the source code would write this instead of `(1.f/255)` ? – M.M Mar 25 '14 at 00:37
  • 3
    @MattMcNabb Maybe they are hoping it will not be evaluated in extended precision. – Navin Mar 25 '14 at 01:25
  • 58
    Mmmm...if only there were some way to avoid magic numbers.... – Paul Draper Mar 25 '14 at 01:29
  • 12
    `1/255 == 0.00(3921568627450980)` -- parens mean repetition. – jfs Mar 25 '14 at 05:06
  • 85
    With your next magic number, [try asking Wolfram Alpha](http://www.wolframalpha.com/input/?i=0.0039215689) – AakashM Mar 25 '14 at 09:31
  • 1
    @J.F.Sebastian Note, that in base-256, the expansion will clearly be `{00}.{01}{01}{01}{01}...` where `{01}` is the single digit "one" in base-256. So in binary it will be `0.(00000001)` where the parens mean repetition. – Jeppe Stig Nielsen Mar 27 '14 at 09:17
  • 1
    @JeppeStigNielsen: in base-256, it is: `0.(1)` (`1` is a more traditional representation of "one" than `{01}`) – jfs Mar 27 '14 at 09:51
  • 15
    whatever the reason, using a magic number without documenting its purpose is very uncool – Isaac Rabinovitch Mar 27 '14 at 19:13
  • @LionC I don't agree with the latest edit on this question. While it can be argued that the question itself has nothing to do with `C`, it has very much to do with `floating-point`. – crush Apr 10 '14 at 12:53
  • @crush Youre probably right, seems I brainfarted there ;-) thx – LionC Apr 10 '14 at 12:55
  • `C` dev have reflexes... optimizing at the place of the compiler. Why is it the first version? Have a problem, then solve it. – Sandburg Sep 13 '18 at 12:35
  • @IsaacRabinovitch, I'm afraid I have received complaints about overcommenting things in code.... there are tastes for everything... but I'm with you. – Luis Colorado Feb 14 '19 at 15:29
  • @LuisColorado, have you ever gotten a complaint for documenting a magic number? That would be a first, but it wouldn't surprise me given the toxicity of the field... – SO_fix_the_vote_sorting_bug Nov 27 '22 at 06:01
  • 1
    @AakashM, there is also the [Inverse Symbol Calculator](https://wayback.cecm.sfu.ca/cgi-bin/isc/lookup?lookup_type=browse&page_no=0&number=.0039215689), but it is less useful in this case because of the rounding. – SO_fix_the_vote_sorting_bug Nov 27 '22 at 06:02
  • @SO_fix_the_vote_sorting_bug, I was asking only. Does the constant respresent something?, then a `#define` would be nice, just to understand that it is not an error (or has some typo) BTW it's used several times with the same value. A typo can be fatal if you change the value from one line to the next. – Luis Colorado Nov 28 '22 at 14:26

2 Answers2

387

0.0039215689 is approximately equal to 1/255.

Seeing that this is OpenGL, performance is probably important. So it's probably safe to guess that this was done for performance reasons.

Multiplying by the reciprocal is faster than repeatedly dividing by 255.


Side Note:

If you're wondering why such a micro-optimization isn't left to the compiler, it's because it is an unsafe floating-point optimization. In other words:

x / 255  !=  x * (1. / 255)

due to floating-point round-off errors.

So while modern compilers may be smart enough to do this optimization, they are not allowed to do it unless you explicitly tell them to via a compiler flag.

Related: Why doesn't GCC optimize a*a*a*a*a*a to (a*a*a)*(a*a*a)?

Community
  • 1
  • 1
Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 1
    Wow. All I can say is awesome answer. I probably would never have discovered this without your knowledge. Of course, now knowing what it represents, it seems quite obvious! – crush Mar 24 '14 at 21:56
  • 10
    I actually didn't know what it was when I first saw it. But seeing the way it was used, I suspected it was the multiply-by-reciprocal optimization. So I checked in my calculator and sure enough - I guessed right. – Mysticial Mar 24 '14 at 22:08
  • Fascinating. I suppose, on a modern processor, multiplication can be faster than a look-up table. Then again, I'd expect a conversion from an 8-bit color channel to float would want a gamma correction (or even an sRGB to linear conversion), in which case a look-up table is probably a win. – Adrian McCarthy Mar 24 '14 at 22:21
  • 57
    I would've expected to see it written as `a = b * (1.0f / 255)`; compilers still do constant folding, don't they? – Ilmari Karonen Mar 24 '14 at 22:51
  • 9
    @IlmariKaronen Yes, they still do constant folding. It's actually required for some stuff like template resolutions and such. But I would've just pulled it out as a constant or a macro. But hey, not all code is perfectly written. :) – Mysticial Mar 24 '14 at 22:54
  • strict floating point adherence... isn't that what the `strictfp` keyword is for (if you need it)? – Bohemian Mar 25 '14 at 02:14
  • 2
    @Bohemian IIRC, the standard allows some leeway in that intermediate results of a floating-point expression can be done at higher precision. `strictfp` turns that off. But to do unsafe floating-point optimizations that require associativity ([such as this](http://stackoverflow.com/questions/6430448/why-doesnt-gcc-optimize-aaaaaa-to-aaaaaa)), you need to tell the compiler to do it. (In GCC, it would be `-ffast-math`.) – Mysticial Mar 25 '14 at 02:19
  • 2
    @Bohemian `strictfp` is not part of the C standard. It exists in Java but one should not mix discussions of Java and C with regard to floating-point, they are each complex enough on their own (and quite different). – Pascal Cuoq Mar 25 '14 at 10:14
  • 1
    It seems strange to me that such things would be using a scale of 255 rather than 256. Am I missing something? – hippietrail Mar 25 '14 at 18:48
  • 5
    @hippietrail Initially, I was wondering the same thing. But if you use 256, it would scale from `0.0 - 0.996` instead the desired `0.0 - 1.0`. (`0.996 = 255/256` where `255` is the largest 8-bit integer) – Mysticial Mar 25 '14 at 18:53
  • 4
    An interesting question could be - why not 0.0039215686 or 0.0039215687, which are closer approximations at the same level of precision? – Daniel Waechter Mar 26 '14 at 00:32
  • 7
    And of course, to answer my own question, it's because the other two numbers can not be represented as standard C floats. The next float below 0.0039215689 is 0.0039215684. – Daniel Waechter Mar 26 '14 at 00:53
  • To be precise, the closest IEEE single-precision floating-point number to 1/255 is 0.0039215688593685626983642578125. – dan04 Mar 26 '14 at 02:46
  • 6
    Of course, a better programmer writing the header file would have defined a constant and used it by name. – Jack Aidley Mar 26 '14 at 13:38
  • 1
    @JackAidley That's the odd thing. I couldn't find a single example online where a programmer defined a constant, yet, this magic number seems to be in widespread use. I actually first encountered it while disassembling an executable that was written in C++. Since then, I've been unable to find a single instance where it's defined as a constant...it might be that it's part of some standard graphics library. – crush Mar 26 '14 at 14:04
  • 1
    It's maybe worth pointing out that there is a similar trick for integers to multiply a reciprocal rather than divide using [magic numbers](http://www.hackersdelight.org/magic.htm). For example n/255 = (0x80808081*n)>>39. – Z boson Apr 04 '14 at 19:12
  • @Mysticial Wait...why is it faster to multiply by the reciprocal than divide by 255? – Rakshith Ravi Nov 20 '15 at 06:45
  • @RakshithRavi Because division is slower than multiplication. – Mysticial Nov 20 '15 at 07:02
  • @Mysticial yeah but why? – Rakshith Ravi Nov 21 '15 at 14:54
  • @RakshithRavi http://stackoverflow.com/questions/15745819/why-is-division-more-expensive-than-multiplication – Mysticial Nov 21 '15 at 17:36
79

This multiplication by 0.0039215689f converts an integer valued color intensity in the range 0 to 255 to a real valued color intensity in the range 0 to 1.

As Ilmari Karonen points out, even if this is an optimisation it's a rather badly expressed one. It would be so much clearer to multiply by (1.0f/255).

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490