public partial struct Decimal
{
// Low level accessors used by a DecCalc and formatting
- internal uint High => (uint)hi;
- internal uint Low => (uint)lo;
- internal uint Mid => (uint)mid;
+ internal uint High => _hi32;
+ internal uint Low => (uint)_lo64;
+ internal uint Mid => (uint)(_lo64 >> 32);
- internal bool IsNegative => flags < 0;
+ internal bool IsNegative => _flags < 0;
- internal int Scale => (byte)(flags >> ScaleShift);
+ internal int Scale => (byte)(_flags >> ScaleShift);
-#if BIGENDIAN
- private ulong Low64 => ((ulong)Mid << 32) | Low;
-#else
- private ulong Low64 => Unsafe.As<int, ulong>(ref Unsafe.AsRef(in lo));
-#endif
+ private ulong Low64 => _lo64;
private static ref DecCalc AsMutable(ref decimal d) => ref Unsafe.As<decimal, DecCalc>(ref d);
private uint uflags;
[FieldOffset(4)]
private uint uhi;
+#if BIGENDIAN
+ [FieldOffset(8)]
+ private uint umid;
+ [FieldOffset(12)]
+ private uint ulo;
+#else
[FieldOffset(8)]
private uint ulo;
[FieldOffset(12)]
private uint umid;
+#endif
/// <summary>
- /// The low and mid fields combined in little-endian order
+ /// The low and mid fields combined
/// </summary>
[FieldOffset(8)]
- private ulong ulomidLE;
+ private ulong ulomid;
private uint High
{
private ulong Low64
{
-#if BIGENDIAN
- get { return ((ulong)umid << 32) | ulo; }
- set { umid = (uint)(value >> 32); ulo = (uint)value; }
-#else
- get => ulomidLE;
- set => ulomidLE = value;
-#endif
+ get => ulomid;
+ set => ulomid = value;
}
private const uint SignMask = 0x80000000;
MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref s, 1)).Fill(0xCD);
}
- #region Decimal Math Helpers
+#region Decimal Math Helpers
private static unsafe uint GetExponent(float f)
{
/// </summary>
internal static int VarDecCmp(in decimal d1, in decimal d2)
{
- if ((d2.Low | d2.Mid | d2.High) == 0)
+ if ((d2.Low64 | d2.High) == 0)
{
- if ((d1.Low | d1.Mid | d1.High) == 0)
+ if ((d1.Low64 | d1.High) == 0)
return 0;
- return (d1.flags >> 31) | 1;
+ return (d1._flags >> 31) | 1;
}
- if ((d1.Low | d1.Mid | d1.High) == 0)
- return -((d2.flags >> 31) | 1);
+ if ((d1.Low64 | d1.High) == 0)
+ return -((d2._flags >> 31) | 1);
- int sign = (d1.flags >> 31) - (d2.flags >> 31);
+ int sign = (d1._flags >> 31) - (d2._flags >> 31);
if (sign != 0)
return sign;
return VarDecCmpSub(in d1, in d2);
private static int VarDecCmpSub(in decimal d1, in decimal d2)
{
- int flags = d2.flags;
+ int flags = d2._flags;
int sign = (flags >> 31) | 1;
- int scale = flags - d1.flags;
+ int scale = flags - d1._flags;
ulong low64 = d1.Low64;
uint high = d1.High;
internal static int GetHashCode(in decimal d)
{
- if ((d.Low | d.Mid | d.High) == 0)
+ if ((d.Low64 | d.High) == 0)
return 0;
- uint flags = (uint)d.flags;
+ uint flags = (uint)d._flags;
if ((flags & ScaleMask) == 0 || (d.Low & 1) != 0)
return (int)(flags ^ d.High ^ d.Mid ^ d.Low);
[Serializable]
[System.Runtime.Versioning.NonVersionable] // This only applies to field layout
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public readonly partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable<decimal>, IEquatable<decimal>, IDeserializationCallback, ISpanFormattable
+ public readonly partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable<decimal>, IEquatable<decimal>, ISpanFormattable, ISerializable, IDeserializationCallback
{
// Sign mask for the flags field. A value of zero in this bit indicates a
// positive Decimal value, and a value of one in this bit indicates a
// and finally bit 31 indicates the sign of the Decimal value, 0 meaning
// positive and 1 meaning negative.
//
- // NOTE: Do not change the order in which these fields are declared. The
- // native methods in this class rely on this particular order.
- // Do not rename (binary serialization).
- private readonly int flags;
- private readonly int hi;
- private readonly int lo;
- private readonly int mid;
+ // NOTE: Do not change the order and types of these fields. The layout has to
+ // match Win32 DECIMAL type.
+ private readonly int _flags;
+ private readonly uint _hi32;
+ private readonly ulong _lo64;
// Constructs a Decimal from an integer value.
//
{
if (value >= 0)
{
- flags = 0;
+ _flags = 0;
}
else
{
- flags = SignMask;
+ _flags = SignMask;
value = -value;
}
- lo = value;
- mid = 0;
- hi = 0;
+ _lo64 = (uint)value;
+ _hi32 = 0;
}
// Constructs a Decimal from an unsigned integer value.
[CLSCompliant(false)]
public Decimal(uint value)
{
- flags = 0;
- lo = (int)value;
- mid = 0;
- hi = 0;
+ _flags = 0;
+ _lo64 = value;
+ _hi32 = 0;
}
// Constructs a Decimal from a long value.
{
if (value >= 0)
{
- flags = 0;
+ _flags = 0;
}
else
{
- flags = SignMask;
+ _flags = SignMask;
value = -value;
}
- lo = (int)value;
- mid = (int)(value >> 32);
- hi = 0;
+ _lo64 = (ulong)value;
+ _hi32 = 0;
}
// Constructs a Decimal from an unsigned long value.
[CLSCompliant(false)]
public Decimal(ulong value)
{
- flags = 0;
- lo = (int)value;
- mid = (int)(value >> 32);
- hi = 0;
+ _flags = 0;
+ _lo64 = value;
+ _hi32 = 0;
}
// Constructs a Decimal from a float value.
DecCalc.VarDecFromR8(value, out AsMutable(ref this));
}
+ private Decimal(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+
+ _flags = info.GetInt32("flags");
+ _hi32 = (uint)info.GetInt32("hi");
+ _lo64 = (uint)info.GetInt32("lo") + ((ulong)info.GetInt32("mid") << 32);
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+
+ // Serialize both the old and the new format
+ info.AddValue("flags", _flags);
+ info.AddValue("hi", (int)High);
+ info.AddValue("lo", (int)Low);
+ info.AddValue("mid", (int)Mid);
+ }
+
//
// Decimal <==> Currency conversion.
//
int f = bits[3];
if (IsValid(f))
{
- lo = bits[0];
- mid = bits[1];
- hi = bits[2];
- flags = f;
+ _lo64 = (uint)bits[0] + ((ulong)(uint)bits[1] << 32);
+ _hi32 = (uint)bits[2];
+ _flags = f;
return;
}
}
{
if (scale > 28)
throw new ArgumentOutOfRangeException(nameof(scale), SR.ArgumentOutOfRange_DecimalScale);
- this.lo = lo;
- this.mid = mid;
- this.hi = hi;
- flags = ((int)scale) << 16;
+ _lo64 = (uint)lo + ((ulong)(uint)mid << 32);
+ _hi32 = (uint)hi;
+ _flags = ((int)scale) << 16;
if (isNegative)
- flags |= SignMask;
+ _flags |= SignMask;
}
void IDeserializationCallback.OnDeserialization(object? sender)
{
// OnDeserialization is called after each instance of this class is deserialized.
// This callback method performs decimal validation after being deserialized.
- if (!IsValid(flags))
+ if (!IsValid(_flags))
throw new SerializationException(SR.Overflow_Decimal);
}
{
if (IsValid(flags))
{
- this.lo = lo;
- this.mid = mid;
- this.hi = hi;
- this.flags = flags;
+ _lo64 = (uint)lo + ((ulong)(uint)mid << 32);
+ _hi32 = (uint)hi;
+ _flags = flags;
return;
}
throw new ArgumentException(SR.Arg_DecBitCtor);
private Decimal(in decimal d, int flags)
{
this = d;
- this.flags = flags;
+ _flags = flags;
}
// Returns the absolute value of the given Decimal. If d is
//
internal static decimal Abs(in decimal d)
{
- return new decimal(in d, d.flags & ~SignMask);
+ return new decimal(in d, d._flags & ~SignMask);
}
// Adds two Decimal values.
// towards positive infinity.
public static decimal Ceiling(decimal d)
{
- int flags = d.flags;
+ int flags = d._flags;
if ((flags & ScaleMask) != 0)
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToPositiveInfinity);
return d;
//
public static decimal Floor(decimal d)
{
- int flags = d.flags;
+ int flags = d._flags;
if ((flags & ScaleMask) != 0)
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToNegativeInfinity);
return d;
//
public static int[] GetBits(decimal d)
{
- return new int[] { d.lo, d.mid, d.hi, d.flags };
+ return new int[] { (int)d.Low, (int)d.Mid, (int)d.High, d._flags };
}
/// <summary>
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}
- destination[0] = d.lo;
- destination[1] = d.mid;
- destination[2] = d.hi;
- destination[3] = d.flags;
+ destination[0] = (int)d.Low;
+ destination[1] = (int)d.Mid;
+ destination[2] = (int)d.High;
+ destination[3] = d._flags;
return 4;
}
return false;
}
- destination[0] = d.lo;
- destination[1] = d.mid;
- destination[2] = d.hi;
- destination[3] = d.flags;
+ destination[0] = (int)d.Low;
+ destination[1] = (int)d.Mid;
+ destination[2] = (int)d.High;
+ destination[3] = d._flags;
valuesWritten = 4;
return true;
}
internal static void GetBytes(in decimal d, byte[] buffer)
{
Debug.Assert(buffer != null && buffer.Length >= 16, "[GetBytes]buffer != null && buffer.Length >= 16");
- buffer[0] = (byte)d.lo;
- buffer[1] = (byte)(d.lo >> 8);
- buffer[2] = (byte)(d.lo >> 16);
- buffer[3] = (byte)(d.lo >> 24);
-
- buffer[4] = (byte)d.mid;
- buffer[5] = (byte)(d.mid >> 8);
- buffer[6] = (byte)(d.mid >> 16);
- buffer[7] = (byte)(d.mid >> 24);
- buffer[8] = (byte)d.hi;
- buffer[9] = (byte)(d.hi >> 8);
- buffer[10] = (byte)(d.hi >> 16);
- buffer[11] = (byte)(d.hi >> 24);
+ Span<byte> span = buffer;
- buffer[12] = (byte)d.flags;
- buffer[13] = (byte)(d.flags >> 8);
- buffer[14] = (byte)(d.flags >> 16);
- buffer[15] = (byte)(d.flags >> 24);
+ BinaryPrimitives.WriteInt32LittleEndian(span, (int)d.Low);
+ BinaryPrimitives.WriteInt32LittleEndian(span.Slice(4), (int)d.Mid);
+ BinaryPrimitives.WriteInt32LittleEndian(span.Slice(8), (int)d.High);
+ BinaryPrimitives.WriteInt32LittleEndian(span.Slice(12), d._flags);
}
internal static decimal ToDecimal(ReadOnlySpan<byte> span)
//
public static decimal Negate(decimal d)
{
- return new decimal(in d, d.flags ^ SignMask);
+ return new decimal(in d, d._flags ^ SignMask);
}
// Rounds a Decimal value to a given number of decimal places. The value
return d;
}
- internal static int Sign(in decimal d) => (d.lo | d.mid | d.hi) == 0 ? 0 : (d.flags >> 31) | 1;
+ internal static int Sign(in decimal d) => (d.Low64 | d.High) == 0 ? 0 : (d._flags >> 31) | 1;
// Subtracts two Decimal values.
//
public static int ToInt32(decimal d)
{
Truncate(ref d);
- if ((d.hi | d.mid) == 0)
+ if ((d.High | d.Mid) == 0)
{
- int i = d.lo;
+ int i = (int)d.Low;
if (!d.IsNegative)
{
if (i >= 0) return i;
public static long ToInt64(decimal d)
{
Truncate(ref d);
- if (d.hi == 0)
+ if (d.High == 0)
{
long l = (long)d.Low64;
if (!d.IsNegative)
public static uint ToUInt32(decimal d)
{
Truncate(ref d);
- if ((d.hi | d.mid) == 0)
+ if ((d.High| d.Mid) == 0)
{
uint i = d.Low;
if (!d.IsNegative || i == 0)
public static ulong ToUInt64(decimal d)
{
Truncate(ref d);
- if (d.hi == 0)
+ if (d.High == 0)
{
ulong l = d.Low64;
if (!d.IsNegative || l == 0)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Truncate(ref decimal d)
{
- int flags = d.flags;
+ int flags = d._flags;
if ((flags & ScaleMask) != 0)
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToZero);
}
public static decimal operator +(decimal d) => d;
- public static decimal operator -(decimal d) => new decimal(in d, d.flags ^ SignMask);
+ public static decimal operator -(decimal d) => new decimal(in d, d._flags ^ SignMask);
public static decimal operator ++(decimal d) => Add(d, One);
public override string ToString() { throw null; }
public string ToString(System.IFormatProvider? provider) { throw null; }
}
- public readonly partial struct Decimal : System.IComparable, System.IComparable<decimal>, System.IConvertible, System.IEquatable<decimal>, System.IFormattable, System.Runtime.Serialization.IDeserializationCallback
+ public readonly partial struct Decimal : System.IComparable, System.IComparable<decimal>, System.IConvertible, System.IEquatable<decimal>, System.IFormattable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable
{
private readonly int _dummyPrimitive;
[System.Runtime.CompilerServices.DecimalConstantAttribute((byte)0, (byte)0, (uint)4294967295, (uint)4294967295, (uint)4294967295)]
public Decimal(int value) { throw null; }
public Decimal(int lo, int mid, int hi, bool isNegative, byte scale) { throw null; }
public Decimal(int[] bits) { throw null; }
- public Decimal(System.ReadOnlySpan<int> bits) { throw null; }
public Decimal(long value) { throw null; }
+ public Decimal(System.ReadOnlySpan<int> bits) { throw null; }
public Decimal(float value) { throw null; }
[System.CLSCompliantAttribute(false)]
public Decimal(uint value) { throw null; }
uint System.IConvertible.ToUInt32(System.IFormatProvider? provider) { throw null; }
ulong System.IConvertible.ToUInt64(System.IFormatProvider? provider) { throw null; }
void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object? sender) { }
+ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
public static byte ToByte(System.Decimal value) { throw null; }
public static double ToDouble(System.Decimal d) { throw null; }
public static short ToInt16(System.Decimal value) { throw null; }