#17 Direct Conversion between BigDecimal (Java) and Decimal (C#)

open
nobody
None
5
2010-10-18
2010-10-18
Anonymous
No

We use .doubleValue() from Java to C# and cast decimal to double in C# to manage decimal to BigDecimal conversion this has some issue about rounding and we must do some ToString() call. This is a quite crapy.

May be implement some implicit or explicit cast will be a great Idea ?

Laurent Barbisan

Discussion

  • Cyprien Noel
    Cyprien Noel
    2011-12-10

    Hi, I wrote something similar for ObjectFabric. It can convert to and from BigDecimal byte array
    (value.unscaledValue().toByteArray()) and scale (value.scale()) to .NET decimal. Max range is
    not the same between the two platforms, but for values supported by both it should be correct.

    public struct DecimalConverter
    {
    private readonly bool _hasValue;
    private readonly byte[] _unscaled;
    private readonly int _scale;

    public DecimalConverter( decimal value )
    {
    _hasValue = true;
    int[] ints = decimal.GetBits( value );
    byte[] bytes = new byte[12];
    writeInt( ints[0], bytes, 0 );
    writeInt( ints[1], bytes, 4 );
    writeInt( ints[2], bytes, 8 );
    BigInteger unscaled = new BigInteger( bytes );
    BigInteger signed = value < 0 ? -unscaled : unscaled;
    _unscaled = signed.ToByteArray();
    BigIntegerConverter.SwitchEndianess( _unscaled );
    _scale = (ints[3] >> 16) & 0xFF;
    }

    public DecimalConverter( bool hasValue, byte[] unscaled, int scale )
    {
    _hasValue = hasValue;
    _unscaled = unscaled;
    _scale = scale;
    }

    public bool HasValue
    {
    get { return _hasValue; }
    }

    public byte[] Unscaled
    {
    get { return _unscaled; }
    }

    public int Scale
    {
    get { return _scale; }
    }

    public static implicit operator decimal?( DecimalConverter value )
    {
    if( !value.HasValue )
    return null;

    BigIntegerConverter.SwitchEndianess( value._unscaled );
    BigInteger signed = new BigInteger( value._unscaled );
    BigInteger unscaled = signed < 0 ? -signed : signed;
    int scale = value._scale;

    if( scale < 0 )
    {
    unscaled = unscaled * BigInteger.Pow( BigInteger.One, -scale + 1 );
    scale = 0;
    }

    byte[] bytes = unscaled.ToByteArray();

    if( bytes.Length > 12 || scale > 28 )
    {
    // Throwing would break the source, e.g. close the network connection
    DotNetLog.write( new OverflowException( "Java BigDecimal was too large for .NET decimal" ) );
    return null;
    }

    byte[] twelve = new byte[12];
    Array.Copy( bytes, 0, twelve, twelve.Length - bytes.Length, bytes.Length );
    return new decimal( readInt( twelve, 0 ), readInt( twelve, 4 ), readInt( twelve, 8 ), signed.Sign < 0, (byte) scale );
    }

    public static implicit operator DecimalConverter( decimal? value )
    {
    if( !value.HasValue )
    return new DecimalConverter( false, null, 0 );

    return new DecimalConverter( value.Value );
    }

    private static int readInt( byte[] array, int index )
    {
    int b0 = array[index + 0] << 24;
    int b1 = array[index + 1] << 16;
    int b2 = array[index + 2] << 8;
    int b3 = array[index + 3] << 0;
    return b3 | b2 | b1 | b0;
    }

    private static void writeInt( int value, byte[] array, int index )
    {
    array[index + 0] = ((byte) ((value >> 0) & 0xFF));
    array[index + 1] = ((byte) ((value >> 8) & 0xFF));
    array[index + 2] = ((byte) ((value >> 16) & 0xFF));
    array[index + 3] = ((byte) ((value >> 24) & 0xFF));
    }
    }