user1878: UPDATE: The code in this question works for all test cases (Hurray!) however I don't like how GetCompact uses the Math.Abs function and don't think it's true to the OpenSSL implementation.
Fixing this issue will probably make this implementation "perfect"
The core issue (I believe) is when .NET does a bit-shift of a negative number, it expands the negative number and because it's stored in Two's compliment, the left shift extra bytes are all ones.
C++ likely does something different when left shifting a BigInteger, because according to the C++ spec, left shifting negative numbers is undefined. 
The solution is to use the corresponding multiplication or division instead of a shift. ... I'm not sure how to do that, so your help would be appreciated.

I'm working on the following C# code, and tried to make it faithful to the original C++ source.  And I'm trying to get this code to match the unit tests described here.
My goal is not only to have a .NET representation of the QT data structures, but also to read and parse JSON-RPC code as well.
C# Tests
       BigInteger bb =   BitcoinQT.SetCompact(numToCompact);


        bb = BitcoinQT.SetCompact(0x00123456);
       /*
       00000000000100100011010001010110 SetCompact:
       00000000011111111111111111111111 Bitmask & (extract 0..23)
       00000000000100100011010001010110 result
       00000000000000000000000000000000 Read bytes 25..32 (>> 24)
       000100100011010001010110 preshifted 24
       00000000 postshifted 24
       00000000100000000000000000000000 ... check bit is neg
       00000000000000000000000000000000 ... Result

       00000000000000000000000000001100 ERROR RESULT SHOULD BE THIS 
        */

C# code
   class BlockTargetBits
{

   static  bool debug = false;

   internal static string GetCompact(BigInteger originalBigNumber)
   {
       // 
       // 
       // Get Compact
       BigInteger num = originalBigNumber;
       byte[] numAsBytes = num.ToByteArray();
       uint compactBitsRepresentation = 0;
       uint size2;// BN_num_bytes(num);
       size2 = (uint)originalBigNumber.NumberOfBytes();
       if (size2 <= 3)
       {
           uint amountToShift2 = 8 * (3 - size2);
           if (debug) Console.WriteLine(GetBits(num) + " will be shifted " + amountToShift2);
           compactBitsRepresentation = (uint)(int)(BigInteger.Abs(num) << (int)amountToShift2);  // HACK: -- ABS MAY NOT BE THE CORRECT THING TO USE HERE
           if (debug) Console.WriteLine(GetBits(compactBitsRepresentation) + " was shifted " + amountToShift2);
       }
       else
       {
           BigInteger bn = num;
           uint amountToShift2 = 8 * (size2 - 3);
           if (debug) Console.WriteLine(GetBits(bn) + " will be shifted " + amountToShift2);
           var bnShifted = BigInteger.Abs(bn) >> (int)amountToShift2;  // HACK: -- ABS MAY NOT BE THE CORRECT THING TO USE HERE
           compactBitsRepresentation = (uint)bnShifted;
       }

       // The 0x00800000 bit denotes the sign.
       // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
       Console.WriteLine(compactBitsRepresentation.ToString("x"));
       if ((compactBitsRepresentation & 0x00800000) != 0)
       {
           compactBitsRepresentation >>= 8;
           size2++;
       }
       if (debug) Console.WriteLine(GetBits(size2) + " size ");

       var tmp = size2 << 24;
       if (debug) Console.WriteLine(GetBits(tmp) + " size (shifted to proper postion)");
       compactBitsRepresentation |= size2 << 24;
       if (debug) Console.WriteLine("21 987654321 987654321 987654321");
       if (debug) Console.WriteLine(GetBits(compactBitsRepresentation) + " size # then compact");

       compactBitsRepresentation |= (num.Sign < 0 ? (uint)0x00800000 : 0);

       if (compactBitsRepresentation == 0)
           return "0";
       return "0x" + compactBitsRepresentation.ToString("x8");
   }


     internal static System.Numerics.BigInteger SetCompact(uint numToCompact)
    {
        if (debug)   Console.WriteLine(GetBits(numToCompact) + " This number will be compacted ");

        //
        //  SetCompact
        // Extract the number from bits 0..23
        if (debug)  Console.WriteLine(GetBits(0x007fffff) + " Bitmask & (extract 0..23) ");

        uint nWord = numToCompact & 0x007fffff;
        if (debug)  Console.WriteLine(GetBits(nWord) + " result ");


        BigInteger ret = new BigInteger(nWord);

        // Add zeroes to the left according to bits 25..32
        var ttt =  ret.ToByteArray();

        uint size = numToCompact >> 24;
        if (debug)  Console.WriteLine(GetBits(size) + " Read bytes 25..32 (>> 24) ");


        uint amountToShift = 0;
        if (size <= 3)
        {
            amountToShift = 8 * (3 - size);
            if (debug)  Console.WriteLine(GetBits(ret) + " preshifted " + amountToShift);

            ret = ret >> (int)amountToShift;
            if (debug) Console.WriteLine( GetBits(ret)+ " postshifted " + amountToShift );
        }
        else
        {
            amountToShift = 8 * (size - 3);
            if (debug) Console.WriteLine(GetBits(ret) + " preshifted " + amountToShift);

            ret = ret << (int)amountToShift;

            if (debug)   Console.WriteLine(GetBits(ret) + " shifted " + amountToShift);
        }

        // Set the value negative if required per bit 24
        if (debug) Console.WriteLine(GetBits(0x00800000) + " ... check bit is neg");

        UInt32 isNegative = 0x00800000 & numToCompact;

        if (debug)  Console.WriteLine(GetBits(isNegative) + " ... Result");

        if (isNegative != 0)
            ret = ret * -1;  

        var test = ret.ToByteArray();
        if (debug) Console.WriteLine(ret + " return");
        if (debug) Console.WriteLine();
        return ret;
    }

    internal static string GetHex(BigInteger bb)
    {
        if (bb == 0)
            return "0";
        else 
        return bb.ToSignedHexString().TrimStart("0".ToCharArray());
    }

    public static string GetBits(BigInteger num)
    {
        return GetBits(num.ToByteArray());

    }
    public static string GetBits(int num)
    {
        return GetBits(BitConverter.GetBytes(num));
    }
    public static string GetBits(uint num)
    {
       return  GetBits(BitConverter.GetBytes(num));
    }
    public static string GetBits(byte[] bytes)
    {
        StringBuilder sb = new StringBuilder();


        int bitPos = (8 * bytes.Length) -1;
        while (bitPos >  -1)
        {
            int byteIndex = bitPos / 8;
            int offset = bitPos % 8;
            bool isSet = (bytes[byteIndex] & (1 << offset)) != 0;

            // isSet = [True] if the bit at bitPos is set, false otherwise
            if (isSet)
                sb.Append("1");
            else
                sb.Append("0");
            bitPos--;
        }


        return sb.ToString();
    }

}


user2306: Here's one thing I'm pretty sure is a bug, in your compact to BigInteger method:
        ret = ret << (int)amountToShift;
        amountToShift = 8 * (size - 3);

Ought to be:
        amountToShift = 8 * (size - 3);
        ret = ret << (int)amountToShift;

EDIT
Found another, in your BigInteger to compact method:
        ret = ret << (int)amountToShift2;

Here's the thing: ret is never used anywhere else. I'm fairly sure you meant to assign to ret2, and cast to an unsigned integer. Remember to fix both instances of this. 
Lemme know if those two suggestions don't fix things.

1878: <p><strong>UPDATE:</strong> The code in this question works for all test cases (Hurray!) however I don't like how <code>GetCompact</code> uses the <code>Math.Abs</code> function and don't think it's true to the OpenSSL implementation.</p>

<p>Fixing this issue will probably make this implementation "perfect"</p>

<p>The core issue (I believe) is when .NET does a bit-shift of a negative number, it expands the negative number and because it's stored in Two's compliment, the left shift extra bytes are all ones.</p>

<p>C++ likely does something different when left shifting a BigInteger, because according to the <a href="https://stackoverflow.com/q/3784996/328397">C++ spec, left shifting negative numbers is undefined.</a> </p>

<p>The solution is to use the corresponding <a href="https://stackoverflow.com/a/15329294/328397">multiplication or division instead of a shift.</a> ... I'm not sure how to do that, so your help would be appreciated.</p>

<hr>

<p>I'm working on the following C# code, and tried to make it faithful to the <a href="https://github.com/bitcoin/bitcoin/blob/master/src/bignum.h" rel="nofollow noreferrer">original C++ source.</a>  And I'm trying to get this code to match <a href="https://bitcoin.stackexchange.com/q/8045/1878">the unit tests described here.</a></p>

<p>My goal is not only to have a .NET representation of the QT data structures, but also to read and parse JSON-RPC code as well.</p>

<p><strong>C# Tests</strong></p>

<pre><code>       BigInteger bb =   BitcoinQT.SetCompact(numToCompact);


        bb = BitcoinQT.SetCompact(0x00123456);
       /*
       00000000000100100011010001010110 SetCompact:
       00000000011111111111111111111111 Bitmask &amp; (extract 0..23)
       00000000000100100011010001010110 result
       00000000000000000000000000000000 Read bytes 25..32 (&gt;&gt; 24)
       000100100011010001010110 preshifted 24
       00000000 postshifted 24
       00000000100000000000000000000000 ... check bit is neg
       00000000000000000000000000000000 ... Result

       00000000000000000000000000001100 ERROR RESULT SHOULD BE THIS 
        */
</code></pre>

<p><strong>C# code</strong></p>

<pre><code>   class BlockTargetBits
{

   static  bool debug = false;

   internal static string GetCompact(BigInteger originalBigNumber)
   {
       // 
       // 
       // Get Compact
       BigInteger num = originalBigNumber;
       byte[] numAsBytes = num.ToByteArray();
       uint compactBitsRepresentation = 0;
       uint size2;// BN_num_bytes(num);
       size2 = (uint)originalBigNumber.NumberOfBytes();
       if (size2 &lt;= 3)
       {
           uint amountToShift2 = 8 * (3 - size2);
           if (debug) Console.WriteLine(GetBits(num) + " will be shifted " + amountToShift2);
           compactBitsRepresentation = (uint)(int)(BigInteger.Abs(num) &lt;&lt; (int)amountToShift2);  // HACK: -- ABS MAY NOT BE THE CORRECT THING TO USE HERE
           if (debug) Console.WriteLine(GetBits(compactBitsRepresentation) + " was shifted " + amountToShift2);
       }
       else
       {
           BigInteger bn = num;
           uint amountToShift2 = 8 * (size2 - 3);
           if (debug) Console.WriteLine(GetBits(bn) + " will be shifted " + amountToShift2);
           var bnShifted = BigInteger.Abs(bn) &gt;&gt; (int)amountToShift2;  // HACK: -- ABS MAY NOT BE THE CORRECT THING TO USE HERE
           compactBitsRepresentation = (uint)bnShifted;
       }

       // The 0x00800000 bit denotes the sign.
       // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
       Console.WriteLine(compactBitsRepresentation.ToString("x"));
       if ((compactBitsRepresentation &amp; 0x00800000) != 0)
       {
           compactBitsRepresentation &gt;&gt;= 8;
           size2++;
       }
       if (debug) Console.WriteLine(GetBits(size2) + " size ");

       var tmp = size2 &lt;&lt; 24;
       if (debug) Console.WriteLine(GetBits(tmp) + " size (shifted to proper postion)");
       compactBitsRepresentation |= size2 &lt;&lt; 24;
       if (debug) Console.WriteLine("21 987654321 987654321 987654321");
       if (debug) Console.WriteLine(GetBits(compactBitsRepresentation) + " size # then compact");

       compactBitsRepresentation |= (num.Sign &lt; 0 ? (uint)0x00800000 : 0);

       if (compactBitsRepresentation == 0)
           return "0";
       return "0x" + compactBitsRepresentation.ToString("x8");
   }


     internal static System.Numerics.BigInteger SetCompact(uint numToCompact)
    {
        if (debug)   Console.WriteLine(GetBits(numToCompact) + " This number will be compacted ");

        //
        //  SetCompact
        // Extract the number from bits 0..23
        if (debug)  Console.WriteLine(GetBits(0x007fffff) + " Bitmask &amp; (extract 0..23) ");

        uint nWord = numToCompact &amp; 0x007fffff;
        if (debug)  Console.WriteLine(GetBits(nWord) + " result ");


        BigInteger ret = new BigInteger(nWord);

        // Add zeroes to the left according to bits 25..32
        var ttt =  ret.ToByteArray();

        uint size = numToCompact &gt;&gt; 24;
        if (debug)  Console.WriteLine(GetBits(size) + " Read bytes 25..32 (&gt;&gt; 24) ");


        uint amountToShift = 0;
        if (size &lt;= 3)
        {
            amountToShift = 8 * (3 - size);
            if (debug)  Console.WriteLine(GetBits(ret) + " preshifted " + amountToShift);

            ret = ret &gt;&gt; (int)amountToShift;
            if (debug) Console.WriteLine( GetBits(ret)+ " postshifted " + amountToShift );
        }
        else
        {
            amountToShift = 8 * (size - 3);
            if (debug) Console.WriteLine(GetBits(ret) + " preshifted " + amountToShift);

            ret = ret &lt;&lt; (int)amountToShift;

            if (debug)   Console.WriteLine(GetBits(ret) + " shifted " + amountToShift);
        }

        // Set the value negative if required per bit 24
        if (debug) Console.WriteLine(GetBits(0x00800000) + " ... check bit is neg");

        UInt32 isNegative = 0x00800000 &amp; numToCompact;

        if (debug)  Console.WriteLine(GetBits(isNegative) + " ... Result");

        if (isNegative != 0)
            ret = ret * -1;  

        var test = ret.ToByteArray();
        if (debug) Console.WriteLine(ret + " return");
        if (debug) Console.WriteLine();
        return ret;
    }

    internal static string GetHex(BigInteger bb)
    {
        if (bb == 0)
            return "0";
        else 
        return bb.ToSignedHexString().TrimStart("0".ToCharArray());
    }

    public static string GetBits(BigInteger num)
    {
        return GetBits(num.ToByteArray());

    }
    public static string GetBits(int num)
    {
        return GetBits(BitConverter.GetBytes(num));
    }
    public static string GetBits(uint num)
    {
       return  GetBits(BitConverter.GetBytes(num));
    }
    public static string GetBits(byte[] bytes)
    {
        StringBuilder sb = new StringBuilder();


        int bitPos = (8 * bytes.Length) -1;
        while (bitPos &gt;  -1)
        {
            int byteIndex = bitPos / 8;
            int offset = bitPos % 8;
            bool isSet = (bytes[byteIndex] &amp; (1 &lt;&lt; offset)) != 0;

            // isSet = [True] if the bit at bitPos is set, false otherwise
            if (isSet)
                sb.Append("1");
            else
                sb.Append("0");
            bitPos--;
        }


        return sb.ToString();
    }

}
</code></pre>
 2306: <p>Here's one thing I'm pretty sure is a bug, in your compact to BigInteger method:</p>

<pre><code>        ret = ret &lt;&lt; (int)amountToShift;
        amountToShift = 8 * (size - 3);
</code></pre>

<p>Ought to be:</p>

<pre><code>        amountToShift = 8 * (size - 3);
        ret = ret &lt;&lt; (int)amountToShift;
</code></pre>

<h2>EDIT</h2>

<p>Found another, in your BigInteger to compact method:</p>

<pre><code>        ret = ret &lt;&lt; (int)amountToShift2;
</code></pre>

<p>Here's the thing: ret is never used anywhere else. I'm fairly sure you meant to assign to ret2, and cast to an unsigned integer. Remember to fix both instances of this. </p>

<p>Lemme know if those two suggestions don't fix things.</p>
