A fixed point decimal data type that has the following qualities: 1) Uses a Int32 to hold the value (4 bytes versus the native decimal's 16 bytes). 2) Defines a fixed number of six digits after the decimal place. 3) Allows a null value to be represented, requiring a single bit to allocated for this. The range of FixedPointDecimal is -1,073.741823 to 1,073.741823. This can therefore represent all possible values in the SQL data type decimal(9,6). The range takes into into account the null bit and the fixed four digits after the decimal place. Fixed point maths also has the benefit of allowing for far simpler/faster comparison.
        /// <summary>
        /// Compares two specified FixedPointDecimal values.
        /// </summary>
        /// <param name="d1">The first <see cref="FixedPointDecimal"/> to compare.</param>
        /// <param name="d2">The second <see cref="FixedPointDecimal"/> to compare.</param>
        /// <returns>A signed integer that indicates the relative values of <paramref name="d1"/> and <paramref name="d2"/>.</returns>
        public static int Compare(FixedPointDecimal d1, FixedPointDecimal d2)
        {
            // Test for null values.
            if ((d1._data & 0x80000000) == 0 && (d2._data & 0x80000000) == 0)
            {   // Both values are null;
                return(0);
            }

            if ((d1._data & 0x80000000) == 0)
            {   // d1 is null.
                return(-1);
            }

            if ((d2._data & 0x80000000) == 0)
            {   // d2 is null.
                return(1);
            }

            // Both values are non null. Extract significands to signed int (and apply sign bit).
            int s1 = (int)(d1._data & 0x3FFFFFFFu | ((d1._data & 0x40000000) << 1));
            int s2 = (int)(d2._data & 0x3FFFFFFFu | ((d2._data & 0x40000000) << 1));

            // Compare signed significands.
            if (s1 > s2)
            {
                return(1);
            }
            else if (s1 < s2)
            {
                return(-1);
            }
            return(0);
        }
Example #2
0
        private static bool InternalTryParse(string s, out FixedPointDecimal result, bool truncateRange)
        {
            // Clean up string. Do quick null/empty test.
            if(null != s)
            {
                s = s.Trim();
            }

            if(String.IsNullOrEmpty(s))
            {
                result = FixedPointDecimal.Null;
                return false;
            }

            // Split string on optional decimal point.
            string[] parts = s.Split('.');
            if(parts.Length > 2)
            {
                result = FixedPointDecimal.Null;
                return false;
            }

            // Test for negative sign.
            bool isNegative;
            if('-' == parts[0][0])
            {
                isNegative = true;
                parts[0] = parts[0].Substring(1);
                if(string.IsNullOrEmpty(parts[0]))
                {
                    result = FixedPointDecimal.Null;
                    return false;
                }
            }
            else
            {
                isNegative = false;
            }

            // Check fractional part is no longer than 6 digits.
            if(parts.Length == 2 && parts[1].Length > 6)
            {
                if(truncateRange)
                {
                    parts[1] = parts[1].Substring(0, 6);
                }
                else
                {
                    result = FixedPointDecimal.Null;
                    return false;
                }
            }

            // Join integer and fractional parts; paddign fractional part to 6 digits if necessary.
            // Parse resulting significand string as integer.
            string significandStr = parts[0] + (parts.Length == 2 ? parts[1].PadRight(6, '0') : "000000");
            uint significand;
            if(!uint.TryParse(significandStr, out significand))
            {
                result = FixedPointDecimal.Null;
                return false;
            }

            // Test significand is within range of FixedPointDecimal.
            if(significand > 1073741823u)
            {
                if(truncateRange)
                {
                    significand = 1073741823u;
                }
                else
                {
                    result = FixedPointDecimal.Null;
                    return false;
                }
            }

            result = new FixedPointDecimal(significand, isNegative);
            return true;
        }
Example #3
0
 /// <summary>
 /// Converts the string representation of a number to its FixedPointDecimal equivalent.
 /// A return value indicates whether the conversion succeeded or failed.
 /// Values outside the range of a FixedPointDecimal are truncated to the min or max values
 /// for FixedPointDecimal as appropriate. Input values with more than four decimal places
 /// have their precision truncated to to four decimal places.
 /// </summary>
 /// <param name="s">The value to parse.</param>
 /// <param name="result">Upon success contains the <see cref="FixedPointDecimal"/> equivalent of the value of <paramref name="s"/>.</param>
 /// <returns><c>true</c> if <paramref name="s"/> could be parsed into a <see cref="FixedPointDecimal"/> instance.</returns>
 public static bool TryParseTruncate(string s, out FixedPointDecimal result)
 {
     return InternalTryParse(s, out result, true);
 }
Example #4
0
 /// <summary>
 /// Determines whether the specified FixedPointDecimal is equal to the current FixedPointDecimal.
 /// </summary>
 /// <param name="d1">The first <see cref="FixedPointDecimal"/> to compare.</param>
 /// <param name="d2">The second <see cref="FixedPointDecimal"/> to compare.</param>
 /// <returns><c>true</c> if the objects are equal, <c>false</c> otherwise.</returns>
 public static bool Equals(FixedPointDecimal d1, FixedPointDecimal d2)
 {
     // We can calculate value equality by testing bitwise equality. This is possible because we
     // are using a fixed point representation *and* we convert -0 to +0 (the only other possible ambiguity).
     return d1._data == d2._data;
 }
Example #5
0
        /// <summary>
        /// Compares two specified FixedPointDecimal values.
        /// </summary>
        /// <param name="d1">The first <see cref="FixedPointDecimal"/> to compare.</param>
        /// <param name="d2">The second <see cref="FixedPointDecimal"/> to compare.</param>
        /// <returns>A signed integer that indicates the relative values of <paramref name="d1"/> and <paramref name="d2"/>.</returns>
        public static int Compare(FixedPointDecimal d1, FixedPointDecimal d2)
        {
            // Test for null values.
            if((d1._data & 0x80000000) == 0 && (d2._data & 0x80000000) == 0)
            {   // Both values are null;
                return 0;
            }

            if((d1._data & 0x80000000) == 0)
            {   // d1 is null.
                return -1;
            }

            if((d2._data & 0x80000000) == 0)
            {   // d2 is null.
                return 1;
            }

            // Both values are non null. Extract significands to signed int (and apply sign bit).
            int s1 = (int)(d1._data & 0x3FFFFFFFu | ((d1._data & 0x40000000) << 1));
            int s2 = (int)(d2._data & 0x3FFFFFFFu | ((d2._data & 0x40000000) << 1));

            // Compare signed significands.
            if(s1 > s2)
            {
                return 1;
            }
            else if(s1 < s2)
            {
                return -1;
            }
            return 0;
        }
 /// <summary>
 /// Determines whether the specified FixedPointDecimal is equal to the current FixedPointDecimal.
 /// </summary>
 /// <param name="d1">The first <see cref="FixedPointDecimal"/> to compare.</param>
 /// <param name="d2">The second <see cref="FixedPointDecimal"/> to compare.</param>
 /// <returns><c>true</c> if the objects are equal, <c>false</c> otherwise.</returns>
 public static bool Equals(FixedPointDecimal d1, FixedPointDecimal d2)
 {
     // We can calculate value equality by testing bitwise equality. This is possible because we
     // are using a fixed point representation *and* we convert -0 to +0 (the only other possible ambiguity).
     return(d1._data == d2._data);
 }
        private static bool InternalTryParse(string s, out FixedPointDecimal result, bool truncateRange)
        {
            // Clean up string. Do quick null/empty test.
            if (null != s)
            {
                s = s.Trim();
            }

            if (String.IsNullOrEmpty(s))
            {
                result = FixedPointDecimal.Null;
                return(false);
            }

            // Split string on optional decimal point.
            string[] parts = s.Split('.');
            if (parts.Length > 2)
            {
                result = FixedPointDecimal.Null;
                return(false);
            }

            // Test for negative sign.
            bool isNegative;

            if ('-' == parts[0][0])
            {
                isNegative = true;
                parts[0]   = parts[0].Substring(1);
                if (string.IsNullOrEmpty(parts[0]))
                {
                    result = FixedPointDecimal.Null;
                    return(false);
                }
            }
            else
            {
                isNegative = false;
            }

            // Check fractional part is no longer than 6 digits.
            if (parts.Length == 2 && parts[1].Length > 6)
            {
                if (truncateRange)
                {
                    parts[1] = parts[1].Substring(0, 6);
                }
                else
                {
                    result = FixedPointDecimal.Null;
                    return(false);
                }
            }

            // Join integer and fractional parts; padding fractional part to 6 digits if necessary.
            // Parse resulting significand string as integer.
            string significandStr = parts[0] + (parts.Length == 2 ? parts[1].PadRight(6, '0') : "000000");
            uint   significand;

            if (!uint.TryParse(significandStr, out significand))
            {
                result = FixedPointDecimal.Null;
                return(false);
            }

            // Test significand is within range of FixedPointDecimal.
            if (significand > 1073741823u)
            {
                if (truncateRange)
                {
                    significand = 1073741823u;
                }
                else
                {
                    result = FixedPointDecimal.Null;
                    return(false);
                }
            }

            result = new FixedPointDecimal(significand, isNegative);
            return(true);
        }
 /// <summary>
 /// Converts the string representation of a number to its FixedPointDecimal equivalent.
 /// A return value indicates whether the conversion succeeded or failed.
 /// Values outside the range of a FixedPointDecimal are truncated to the min or max values
 /// for FixedPointDecimal as appropriate. Input values with more than four decimal places
 /// have their precision truncated to four decimal places.
 /// </summary>
 /// <param name="s">The value to parse.</param>
 /// <param name="result">Upon success contains the <see cref="FixedPointDecimal"/> equivalent of the value of <paramref name="s"/>.</param>
 /// <returns><c>true</c> if <paramref name="s"/> could be parsed into a <see cref="FixedPointDecimal"/> instance.</returns>
 public static bool TryParseTruncate(string s, out FixedPointDecimal result)
 {
     return(InternalTryParse(s, out result, true));
 }