20

How does a .NET decimal type get represented in binary in memory?

We all know how floating-point numbers are stored and the thusly the reasons for the inaccuracy thereof, but I can't find any information about decimal except the following:

  1. Apparently more accurate than floating-point numbers
  2. Takes 128 bits of memory
  3. 2^96 + sign range
  4. 28 (sometimes 29?) total significant digits in the number

Is there any way I can figure this out? The computer scientist in me demands the answer and after an hour of attempted research, I cannot find it. It seems like there's either a lot of wasted bits or I'm just picturing this wrong in my head. Can anyone shed some light on this please?

Matthew
  • 1,905
  • 3
  • 19
  • 26
Squirrelsama
  • 5,480
  • 4
  • 28
  • 38

2 Answers2

40

Decimal.GetBits for the information you want.

Basically it's a 96 bit integer as the mantissa, plus a sign bit, plus an exponent to say how many decimal places to shift it to the right.

So to represent 3.261 you'd have a mantissa of 3261, a sign bit of 0 (i.e. positive), and an exponent of 3. Note that decimal isn't normalized (deliberately) so you can also represent 3.2610 by using a mantissa of 32610 and an exponent of 4, for example.

I have some more information in my article on decimal floating point.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    +1 fantastic answer, right to the point and rich with information. – JoshD Sep 27 '10 at 06:15
  • @Jacob: No, that's *clearly* not true. Given that you can start with an integer and end up with (say) 0.1, that's obviously shifting right. If you could *only* shift to the left, you'd be able to represent 10, 100 etc - but not 0.1, 0.01 etc. In future, please wait for an "ack" before you change an answer's meaning significantly like this. See the documentation for `decimal`: http://msdn.microsoft.com/en-us/library/1k2e8atx.aspx - where the scaling factor is talked about as *dividing* the integer, which is equivalent to shifting it to the right. – Jon Skeet Feb 13 '13 at 06:45
  • My apologies. To me it's more intuitive to think of it as shifting the *decimal point* to the *left*, but I see now that you're referring to "shifting" the mantissa. – Jacob Feb 13 '13 at 15:59
  • From the book: «bits 16-23 (the low bits of the high 16-bit word) contain the exponent» – does this mean that the exponent has 8 bits and therefore it is possible to shift maximally 255 decimal places to the right? – nalply Apr 21 '13 at 09:08
  • @nalply: 8 bits are effectively reserved for the exponent, but it has a limit placed on it so the range is actually only 0-29. – Jon Skeet Apr 21 '13 at 09:22
  • This answer makes it much clearer than the linked article. Even though the article gives the exact formula, an example is always great for understanding. – Felix Dombek Aug 26 '15 at 12:24
  • So there's 96 bits for the mantissa and one bit for the sign. I'd then expect five bits for the exponent of 0-28, and I'd expect 26 unused bits to give a total of 128. But according to that MSDN page there's actually only 23 unused bits, because the exponent is represented with eight bits instead of the five it needs. I'm guessing then that the highest three bits of the exponent are unused and must be zero as well? Or are they just ignored if they're not zero? – Adam Goodwin Feb 09 '16 at 05:36
  • @AdamGoodwin: I wouldn't like to guess what happens if they're not zero - although the docs for the constructor taking the constituent parts says it throws an exception if the scale is more than 28. – Jon Skeet Feb 09 '16 at 06:58
1

https://www.csharpindepth.com/Articles/Decimal

How is a decimal stored?

A decimal is stored in 128 bits, even though only 102 are strictly necessary. It is convenient to consider the decimal as three 32-bit integers representing the mantissa, and then one integer representing the sign and exponent. The top bit of the last integer is the sign bit (in the normal way, with the bit being set (1) for negative numbers) and bits 16-23 (the low bits of the high 16-bit word) contain the exponent. The other bits must all be clear (0). This representation is the one given by decimal.GetBits(decimal) which returns an array of 4 ints.

Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Jevgenij
  • 11
  • 1