开发者

double parsing algorithm in C#

Does any开发者_高级运维one know where to find source code for parsing doubles? I'm looking for the algorithm, not the standard "double.Parse" or "double.TryParse" methods.


Microsoft's source code is available for online browsing. The double parsing code is in System.Number.ParseNumber. It's definitely non-trivial, though, nearly 200 lines long.

You could also use Reflector to see it, though the real code is easier to understand, and has proper comments. You can also download the source for local browsing, by clicking the 'download' link at the top of that page.

Note that this code is copyright Microsoft, so I don't believe you'd be able to use it for anything other than satisfying your curiosity or debugging your application. See http://referencesource.microsoft.com/license.html


As an addition to David yaw´s answer with a link to MS .Net Framework source code, I've here the Mono source code. This can be viewed online:

System.Double source code

This code may also be more "free" than MS code.


Here is the link to Double.Parse in the .Net Common Language Specification, which may be as close as you are going to get to the inner workings of Double.Parse.


Here's two different methods. One uses a regular expression to crack a numeric literal; the other does it old-school:

using System;
using System.Text.RegularExpressions;

namespace ConsoleApplication11
{
    class Program
    {

        static int Main( string[] argv )
        {
            double value1 = parseWithRegex(      "+3.1415926E+09" ) ;
            double value2 = parseWithBruteForce( "+3.1415926E+09" ) ;

            return 0 ;

        }

        static Regex rxNumericValue = new Regex( @"
^                     # start of line, followed by
\s*                   # zero or more leading whitespace characters, followed by
(?<sign>[+-])?        # an optional sign (either '+' or '-'), followed by
(?<integer>[0-9]+)    # a mandatory integer value (1 or more decimal digits), followed by
(                     # an optional fraction,
  \.                  #   consisting of a decimal point, followed by
  (?<fraction>[0-9]*) #   zero or more decimal digits
)?                    # The optional franction is followed by
(                     # an optional exponent,
  [Ee]                #   consisting of the letter E, followed by
  (?<expsign>[+-])?   #   an optional sign (either '+' or '-'), followed by
  (?<exponent>[0-9]+) #   1 or more decimal digits
)?                    # The optional exponent is followed by
\s*                   # zero more more trailing whitespace characters
" ,
  RegexOptions.IgnorePatternWhitespace|RegexOptions.ExplicitCapture
  );
        /// <summary>
        /// Parse a numeric literal into a double with a regular expression
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static double parseWithRegex( string s )
        {
            if ( s == null ) throw new ArgumentNullException("s") ;

            Match match = rxNumericValue.Match( s ) ;
            if ( !match.Success ) throw new FormatException() ;

            string sign           = match.Groups[ "sign"     ].Value ;
            string integerDigits  = match.Groups[ "integer"  ].Value ;
            string fractionDigits = match.Groups[ "fraction" ].Value ;
            string exponentSign   = match.Groups[ "expsign"  ].Value ;
            string exponentDigits = match.Groups[ "exponent" ].Value ;
            double accumulator    = 0.0 ;

            foreach ( char digit in integerDigits )
            {
                accumulator *= 10.0 ;
                accumulator += (digit - '0') ; // assumes codepoints for 0-9 are contiguous
            }
            double factor = 1.0 ;
            foreach ( char digit in fractionDigits )
            {
                factor      *= 10.0 ;
                accumulator += ((double)( digit - '0' )) / factor ;
            }
            if ( sign == "-" ) accumulator = - accumulator ;

            int power = 0 ;
            foreach ( char digit in exponentDigits )
            {
                power *= 10 ;
                power += ( digit - '0') ;
            }
            if ( exponentSign == "-" ) power = - power ;

            double exponent = Math.Pow( 10.0 , power ) ;
            double value    = accumulator *= exponent ;
            return value ;
        }

        /// <summary>
        /// Parse a numeric literal into a double the old-fashioned way
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static double parseWithBruteForce( string s )
        {
            bool   isNegative         ;
            double accumulator        = 0.0 ;
            int    i                  = 0 ;

            // skip lead-in whitespace
            while ( i < s.Length && char.IsWhiteSpace(s[i]) )
            {
                ++i ;
            }

            // parse the sign
            if ( i >= s.Length ) throw new FormatException() ;
            switch ( s[i] )
            {
            case '+' : isNegative = false ; ++i ; break ;
            case '-' : isNegative = true  ; ++i ; break ;
            default  : isNegative = false ; break ;
            }

            // parse the integer portion
            if ( i >= s.Length ) throw new FormatException() ;
            bool hasIntegerDigits = false ;
            while ( i < s.Length && char.IsDigit(s[i]) )
            {
                hasIntegerDigits  = true ;
                accumulator      *= 10.0 ;
                accumulator      += ( s[i] - '0') ;
                ++i ;
            }
            if ( !hasIntegerDigits ) throw new FormatException() ;

            // set the sign
            if ( isNegative ) accumulator = - accumulator ;

            // from this point on, everything is optional

            // parse the fraction, if there is one
            if ( i < s.Length && s[i] == '.' )
            { // got a decimal point
                ++i ; //gobble the decimal point
                double factor = 1.0 ;
                while ( i < s.Length && char.IsDigit(s[i]) )
                {
                    factor      *= 10.0 ;
                    accumulator += ((double)( s[i] - '0' )) / factor ;
                    ++i ;
                }
            }

            // parse the exponent, if there is one
            if ( i < s.Length && ( s[i] == 'E' || s[i] == 'e' ) )
            { // found an exponent
                ++i ; // gobble the 'E'

                // parse the sign
                if ( i >= s.Length ) throw new FormatException() ;
                bool expNegative ;
                switch ( s[i] )
                {
                case '+' : expNegative = false ; ++i ; break ;
                case '-' : expNegative = true  ; ++i ; break ;
                default  : expNegative = false ;       break ;
                }

                bool hasExponentDigits = false ;
                int power = 0 ;
                while ( i < s.Length && char.IsDigit(s[i]) )
                {
                    hasExponentDigits  = true ;
                    power             *= 10 ;
                    power             += (s[i] - '0') ;
                    ++i ;
                }
                if ( !hasExponentDigits ) throw new FormatException() ;
                if ( expNegative ) power = - power ;

                double exponent  = Math.Pow(10.0,power) ;
                accumulator     *= exponent ;

            }

            // skip past any trailing whitespace
            while ( i < s.Length && char.IsWhiteSpace(s[i]) )
            {
                ++i ;
            }

            // if we're not at end-of-string, we have a syntax error
            if ( i < s.Length ) throw new FormatException() ;

            return accumulator ;
        }


    }

}


You could crack open the code for System.Double in Reflector. This boils down to a pretty hairy unsafe method called "ParseNumber". I'm glad I didn't have to implement that. :-)

Another option would be to look at System.Double in Mono (http://www.mono-project.com/Main_Page).


Well, here's code that parses integer values.

I wouldn't think it would take much effort to put in handling to detect the decimal point, and modify the parsing of digits that come after it.

Perhaps you should clarify which part of this is giving you trouble.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜