Hello,
On Day33 of Handmade hero at this timestamp: https://youtu.be/iHSAOSYOt9E?list=PLnuhp3Xd9PYTt6svyQPyRO_AAuMWGxPzU&t=832
Casey Muratori explains here what would happen if we tried to store where stuff was in the world in 32-bit values. I did not understand this explanation. He references that he said something about this before but I don't know in which video (I admit that I skip the Q&A each day). I'm hoping someone would be kind enough to explain this here:
Why do we lose the first 8 bits of the 32bit value? He says something about there being an exponent and a mantissa, but I don't understand why that would remove the 8 bits (assuming the mantissa part is unusable, why is it unusable)?
He says we need another 8 bits to store color and that it will be required to tell how far we are "through that pixel". I don't get this at all. What does distance have to do with color and won't we need 3 channels for color, each of which is going to be 8 bits? Also, why would we talk about color when talking about location (im guessing its something about anti-aliasing which I know nothing about). I also don't know what "the full color range of blending" or "alpha blending" means.
I realize I may be asking something that warrants a much bigger explanation than is possible in a forum post. So, if you can point me to episodes I should watch (I have skipped all Q&As) or some other resource I can read I can go through them.
The 32 bit values are single precision floating point. Those are standardized to 24 bits of mantissa and 7 bits of exponant, and 1 bit of sign. It's the mantissa that is usable while all the rest isn't for purposes of precision.
IIRC That is about blending and getting a smooth blend. For blending there are 8 bits of alpha to pass the information to blend with.
1
The mantissa part is the part that is usable. The floating point number mantissa is the "information" part of the number, for example 1.234
(in base 10, but in the floating point number it's represented in base 2). It's multiplied by the base (2) to the power of exponent
(exponent takes 8 bits in the float, and is biased meaning you need to transform it to get its actual value) and the sign (1 bit in the float). The floating point number as a mantissa of 23 bits, but the first bit is assume to be 1 when the exponent is not 0, and 0 if the exponent is 0, meaning that in practice there is 24 bit of precision for the mantissa.
number = sign * mantissa * 2^exponent
Since you multiply the mantissa by the exponent, that means that you can add zeroes in front or after the mantissa, but not add more "number"/precision/information.
2
Blending means mixing two (or more) values together using a factor. In the case of alpha blending, we are talking about transparency/opacity of an image in front of another image. Alpha is the 4th component in color (Red Green Blue Alpha, RGBA) and is the factor use to blend the color from the two images (the sprite and the background for example).
Here what Casey means is that when the edge of an image is over a pixel of another image, it doesn't cover it entirely and we want to compute a value that represent how much of the image is covered, and we store/call that an alpha value. Since each color channel (R, G, B) is 8 bit, we only need 8 bit of alpha as more precision wouldn't be representable by the color. So Casey wants to use 8 of the 24bits available in the mantissa to represent the alpha value.
In the end that means we can use 16bits of the mantissa for the position in the world, and 8bits from the mantissa for sub pixel precision. Note that this doesn't mean that we would encode things specially, but we would do computation so that we would always have at least 8bits of precision after the decimal point.
If you want to search for things in handmade hero you can use the episode guide at the top of the forum.
Ah I see now. So the 8bits for the alpha channel represent in some sense how much of a pixel is covered. I wasn't able to make that association from the video because I was not seeing that the colors are actually stored somewhere else and this alpha is a modifier telling us something about the position of the item in the world.
I also see why only 24 bits are usable in the real32 value. I was thinking that we are going to do some kind of exotic operations to fill in the bits of the location we are trying to encode into the entire 32bits available. Therefore, I did not see why we would not want to use the first 8 bits as well. But what I am understanding right now, is that when I declare some memory as being a real32 its not just an array of 32 bits, but there are special sections in it. And (this I'm not totally sure about), just simple bitwise operations like the kind we did to pack in the RGBA values into uint32 values to draw the weirdgradient will not work on a float. We must necessarily use the first 7 bits to store the value of the exponent. And that means we can only store 24 bits of precision. And in physical space we want to say that if our location is say 0.82343meters. Then we will use 0.823 to find the pixel on the screen where to set the color of this item and then the last 43
would represent precision that is sub-pixel and will be encoded as the alpha value of that pixel. So we decide that this last 43
part of the number in the example above will be 8 bits in length. And the remaining part of the 24 bits will represent the pixel position. And then Casey Muratori is doing the computation to see how much "world" can be represented like this.
So yeah, I sort of get it now. Ill have to go through the whole thing again but thankyou for explaining this!
Damn! That was way deeper than I had thought :P. If I am right now, I had no idea he was doing that before... This is awesome :D
We must necessarily use the first 7 bits to store the value of the exponent.
The exponent is 8 bits and uses bits 23 to 30 (included and 0 is the first bit), see the Single-precision floating-point format article on wikipedia.
You can extract the sign, exponent and mantissa using bit manipulations:
float f = 0.001234f; uint32_t u = *( u32* ) &f; /* Bit copy. */ uint8_t sign = ( uint8_t ) ( u >> 31 ); uint8_t exponent = ( uint8_t ) ( ( u >> 23 ) & 0xff ); uint32_t mantissa = u & 0x7fffff;