0

I have an array of 4 unsigned int entries of the same value 255 I would like to convert them into a decimal value. Here is the code:

public static decimal BytesToDecimal(byte[] buffer, int offset = 0)
{
    var decimalBits = new int[4];

    decimalBits[0] = buffer[offset + 0] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24);
    decimalBits[1] = buffer[offset + 4] | (buffer[offset + 5] << 8) | (buffer[offset + 6] << 16) | (buffer[offset + 7] << 24);
    decimalBits[2] = buffer[offset + 8] | (buffer[offset + 9] << 8) | (buffer[offset + 10] << 16) | (buffer[offset + 11] << 24);
    decimalBits[3] = buffer[offset + 12] | (buffer[offset + 13] << 8) | (buffer[offset + 14] << 16) | (buffer[offset + 15] << 24);

    return new decimal(decimalBits);
 }

static void Main(string[] args)
{           
    decimal dd = BytesToDecimal(new byte[16] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 });
     Console.WriteLine("{0}", dd.ToString());           
 }

I expect the result -1; however, the program throws the following exception.

Unhandled Exception: System.ArgumentException: Decimal byte array constructor requires an array of length four containing val id decimal bytes.

SWalters
  • 3,615
  • 5
  • 30
  • 37
Hello Everyone
  • 329
  • 4
  • 11
  • Is this part of any cross-platform code? To my knowledge, the internal representation of `System.Decimal` is not portable. – Dai Aug 04 '17 at 05:10

3 Answers3

2

According to the constructor documentation for decimal, bits 0 through 15, and 24 through 30, of the fourth int array element must all be zero. 255, as an 8-bit binary number, is all ones. So you're feeding the constructor an array of ints that have every bit set to 1.

Jeff
  • 7,504
  • 3
  • 25
  • 34
  • You cannot pass arbitrary int arrays through to this constructor, it's intended for the output of decimal.GetBits method. If you want to pass in your own int arrays you have to read the documentation for the decimal type & understand how it encodes its data. It's not a simple exponents followed by addition algorithm. – Ashley Pillay Aug 04 '17 at 05:12
0

Your error is in the assumption that if you set all bits of a decimal, the result will be -1. This is not how decimals are stored.

Your method would be correct for integers, but not for a fixed point number like a decimal. A decimal does not only store the base number (the. mantissa), but also the position of the decimal dot. Also, the sign is set differently in a decimal, since the mantissa is unsigned.

Check this Q&A for further information on how to format a decimal.

Sefe
  • 13,731
  • 5
  • 42
  • 55
0

According to documentation Decimal(Int32[]) last array element used as flag.

bits 2 contains the scale factor and sign, and consists of following parts:

Bits 0 to 15, the lower word, are unused and must be zero.

Bits 16 to 23 must contain an exponent between 0 and 28, which indicates the power of 10 to divide the integer number.

Bits 24 to 30 are unused and must be zero.

Bit 31 contains the sign; 0 meaning positive, and 1 meaning negative.

Integer value -1 in binary format Convert.ToString(-1, 2); equals to

11111111111111111111111111111111

And this value doesn't match

private const int SignMask  = unchecked((int)0x80000000);

or

private const int ScaleMask = 0x00FF0000;

masks, that is why in source code when you pass -1, condition

(f & ~(SignMask | ScaleMask)) == 0 && (f & ScaleMask) <= (28 << 16))

returns false and the next command throw the exception.


Update

if you wish to find out how value -1 represents in Decimal you could use Decimal.GetBits method

string.Join(" ", decimal.GetBits((decimal) -1))

Result will be:

1 0 0 -2147483648

.netFiddle

Community
  • 1
  • 1
Max
  • 1,070
  • 3
  • 13
  • 19