What does the constant 0.0039215689 represent?

20,902

Solution 1

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)?

Solution 2

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).

Share:
20,902
crush
Author by

crush

Programming since I was 13 years old. Everyday, try to learn something new. Everyday, learning just how little I know. All views expressed here are my own, and not those of my employer. Languages & Technologies: C++, C#, ASP.NET, Java, JavaScript, JSON, PHP, XML, CSS, HTML, SQL. Dabbled in more than I could name. Official developer for the Star Wars Galaxies Emulator project since 2006. Topics on Meta that I condone: Please, be nice to new users Using KBD for anything but defining a keyboard sequence is abuse. Code only answers only tell the how, and not the why.

Updated on April 11, 2020

Comments

  • crush
    crush about 4 years

    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.

    • xav
      xav about 10 years
      Note: Color components (e.g. red, green, blue, alpha) are often represented as an integer in [0 ; 255] in many APIs
    • M.M
      M.M about 10 years
      Is there any reason the source code would write this instead of (1.f/255) ?
    • Navin
      Navin about 10 years
      @MattMcNabb Maybe they are hoping it will not be evaluated in extended precision.
    • Paul Draper
      Paul Draper about 10 years
      Mmmm...if only there were some way to avoid magic numbers....
    • jfs
      jfs about 10 years
      1/255 == 0.00(3921568627450980) -- parens mean repetition.
    • AakashM
      AakashM about 10 years
      With your next magic number, try asking Wolfram Alpha
    • crush
      crush about 10 years
      @AakashM Awesome. Thanks for that. That's really neat.
    • Jeppe Stig Nielsen
      Jeppe Stig Nielsen about 10 years
      @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.
    • jfs
      jfs about 10 years
      @JeppeStigNielsen: in base-256, it is: 0.(1) (1 is a more traditional representation of "one" than {01})
    • Isaac Rabinovitch
      Isaac Rabinovitch about 10 years
      whatever the reason, using a magic number without documenting its purpose is very uncool
    • crush
      crush about 10 years
      @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.
    • LionC
      LionC about 10 years
      @crush Youre probably right, seems I brainfarted there ;-) thx
    • Sandburg
      Sandburg over 5 years
      C dev have reflexes... optimizing at the place of the compiler. Why is it the first version? Have a problem, then solve it.
    • Luis Colorado
      Luis Colorado over 5 years
      @IsaacRabinovitch, I'm afraid I have received complaints about overcommenting things in code.... there are tastes for everything... but I'm with you.
  • crush
    crush about 10 years
    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!
  • Mysticial
    Mysticial about 10 years
    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.
  • Adrian McCarthy
    Adrian McCarthy about 10 years
    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.
  • Ilmari Karonen
    Ilmari Karonen about 10 years
    I would've expected to see it written as a = b * (1.0f / 255); compilers still do constant folding, don't they?
  • Mysticial
    Mysticial about 10 years
    @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. :)
  • Bohemian
    Bohemian about 10 years
    strict floating point adherence... isn't that what the strictfp keyword is for (if you need it)?
  • Mysticial
    Mysticial about 10 years
    @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), you need to tell the compiler to do it. (In GCC, it would be -ffast-math.)
  • Pascal Cuoq
    Pascal Cuoq about 10 years
    @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).
  • hippietrail
    hippietrail about 10 years
    It seems strange to me that such things would be using a scale of 255 rather than 256. Am I missing something?
  • Mysticial
    Mysticial about 10 years
    @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)
  • Johny
    Johny about 10 years
    Or maybe better, defined as constant?
  • David Heffernan
    David Heffernan about 10 years
    @Johny Certainly defined as a constant. The point being not a magic value.
  • Jeffrey
    Jeffrey about 10 years
    An interesting question could be - why not 0.0039215686 or 0.0039215687, which are closer approximations at the same level of precision?
  • Jeffrey
    Jeffrey about 10 years
    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.
  • dan04
    dan04 about 10 years
    To be precise, the closest IEEE single-precision floating-point number to 1/255 is 0.0039215688593685626983642578125.
  • Jack Aidley
    Jack Aidley about 10 years
    Of course, a better programmer writing the header file would have defined a constant and used it by name.
  • crush
    crush about 10 years
    @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.
  • Z boson
    Z boson about 10 years
    It's maybe worth pointing out that there is a similar trick for integers to multiply a reciprocal rather than divide using magic numbers. For example n/255 = (0x80808081*n)>>39.
  • Rakshith Ravi
    Rakshith Ravi over 8 years
    @Mysticial Wait...why is it faster to multiply by the reciprocal than divide by 255?
  • Mysticial
    Mysticial over 8 years
    @RakshithRavi Because division is slower than multiplication.
  • Rakshith Ravi
    Rakshith Ravi over 8 years
    @Mysticial yeah but why?
  • Mysticial
    Mysticial over 8 years