strtod.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 644 行 · 第 1/2 页

C
644
字号
/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  String to double/long_double conversion.
*
****************************************************************************/


#include "variety.h"
#include "widechar.h"
#include "libsupp.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <inttypes.h>
#ifdef __WIDECHAR__
    #include <wchar.h>
#endif
#include "watcom.h"
#include "xfloat.h"


typedef enum {
    NEGATIVE_NUMBER   = 0x01,
    NEGATIVE_EXPONENT = 0x02,
    DIGITS_PRESENT    = 0x04,
    DOT_FOUND         = 0x08,
    HEX_FOUND         = 0x10,
    NAN_FOUND         = 0x20,
    INFINITY_FOUND    = 0x40,
    INVALID_SEQ       = 0x80
} flt_flags;

enum {
    _ZERO       = 0,
    _ISNAN,
    _ISINF,
    _NONZERO,
    _UNDERFLOW,
    _OVERFLOW,
    _NEGATIVE   = 0x80
};

#ifdef _LONG_DOUBLE_
 #define MAX_DIGITS     19
#else
 #define MAX_DIGITS     18
#endif

/* Size of buffer holding significant digits. Must be large enough
 * to support the highest precision type.
 */
#define MAX_SIG_DIG     32

#if MAX_SIG_DIG < MAX_DIGITS
    #error MAX_SIG_DIG is too small
#endif

/* Maximum hex digits */
#define MAX_HEX_DIGITS  16

#if !(defined(_LONG_DOUBLE_) || defined(__WIDECHAR__))
void __ZBuf2LD( char _WCNEAR *buf, long_double _WCNEAR *ld )
{
    int         i;
    int         n;
    long        high;
    long        low;
    CHAR_TYPE   *ptr;

    ptr = (CHAR_TYPE *)buf;
    n = __F_NAME(strlen,wcslen)( ptr );
    high = 0;
    for( i = 0; i <= 8; i++ ) {         // collect high 9 significant digits
        if( n <= 9 ) break;
        --n;
        high = high * 10 + (*ptr++ - '0');
    }
    low = 0;
    for( i = 0; i <= 8; i++ ) {         // collect low 9 significant digits
        if( n == 0 ) break;
        --n;
        low = low * 10 + (*ptr++ - '0');
    }
    if( high == 0 && low == 0 ) {
        ld->value = 0.0;
    } else {
        ld->value = (double)high * 1e9 + (double)low;
    }
}
#endif

static void __ZXBuf2LD( char _WCNEAR *buf, long_double _WCNEAR *ld, int *exponent )
{
    int         i;
    int         n;
    uint32_t    xdigit;
    int32_t     exp = *exponent;
    char        *s = buf;
    uint32_t    high = 0;
    uint32_t    low = 0;

    n = strlen( s );
    for( i = 0; i < 8; i++ ) {     /* collect high 8 significant hex digits */
        if( n == 0 ) break;
        --n;
        xdigit = isdigit( *s ) ? xdigit = *s++ - '0' : tolower( *s++ ) - 'a' + 10;
        high |= xdigit << (28 - i * 4);
        exp += 4;
    }

    for( i = 0; i < 8; i++ ) {     /* collect low 8 significant hex digits */
        if( n == 0 ) break;
        --n;
        xdigit = isdigit( *s ) ? xdigit = *s++ - '0' : tolower( *s++ ) - 'a' + 10;
        low |= xdigit << (28 - i * 4);
        exp += 4;
    }

    /* Flush significand to the left */
    while( !(high & 0x80000000) ) {
        high <<= 1;
        if( low & 0x80000000 )
            high |= 1;
        low <<= 1;
        --exp;
    }

    /* Perform final conversion to binary */
    --exp;
#ifdef _LONG_DOUBLE_
    ld->low_word  = low;
    ld->high_word = high;
    ld->exponent  = 0x3FFF + exp;                   /* bias is 16383 (0x3FFF) */
#else

    /* The msb is implied and not stored. Shift the significand left once more */
    high <<= 1;
    if( low & 0x80000000 )
        high |= 1;
    low <<= 1;

    ld->word[I64LO32]  = (high << (32 - 12)) | (low >> 12);
    ld->word[I64HI32]  = high >> 12;
    ld->word[I64HI32] |= (0x3FF + exp) << (32 - 12); /* bias is 1023 (0x3FF) */
#endif
    *exponent = exp;
}

/* Determine what sort of subject sequence, if any, is provided, and parse
 * NaNs and Infinity.
 */
static flt_flags subject_seq( const CHAR_TYPE *s, const CHAR_TYPE **endptr )
{
    CHAR_TYPE           chr;
    flt_flags           flags = 0;

    /* Skip whitespace */
    for( chr = *s; isspace( chr ); chr = *++s )
        ;

    /* Parse optional sign */
    if( chr == '+' ) {
        ++s;
    } else if( chr == '-' ) {
        flags |= NEGATIVE_NUMBER;
        ++s;
    }

    if( *s == 'n' || *s == 'N' ) {
        /* Could be a NaN */
        if( (*++s == 'a' || *s == 'A')
         && (*++s == 'n' || *s == 'N') ) {
            /* Yup, it is. Skip the optional digit/nondigit sequence */
            flags |= NAN_FOUND;
            if( *++s == '(' ) {
                const CHAR_TYPE     *p = s;

                p = ++s;
                while( isalnum( *p ) || *p == '_' )
                    ++p;

                if( *p == ')' )
                    s = ++p;    /* only update s if valid sequence found */
            }
        } else {
            flags = INVALID_SEQ;
        }
    } else if ( *s == 'i' || *s == 'I' ) {
        /* Could be infinity */
        if( (*++s == 'n' || *s == 'N')
         && (*++s == 'f' || *s == 'F') ) {
            const CHAR_TYPE     *p = s;

            /* Yup, it is. See if it's the long form */
            flags |= INFINITY_FOUND;
            if( (*++p == 'i' || *p == 'I')
             && (*++p == 'n' || *p == 'N')
             && (*++p == 'i' || *p == 'I')
             && (*++p == 't' || *p == 'T')
             && (*++p == 'y' || *p == 'Y') ) {

                s = p;
            }
            ++s;
        } else {
            flags = INVALID_SEQ;
        }
    } else if( s[0] == '0' && (s[1] == 'x' || s[1] == 'X') ) {
        /* We've been hexed! */
        flags |= HEX_FOUND;
        s += 2;
    }
    *endptr = s;

    return( flags );
}

/* Parse a decimal floating-point number: collect siginficant digits
 * into buffer and determine exponent (power of ten).
 */
static flt_flags parse_decimal( const CHAR_TYPE *input, CHAR_TYPE *buffer,
                                const CHAR_TYPE **endptr, int *exp,
                                int *sig, int maxdigits )
{
    CHAR_TYPE           chr;
    flt_flags           flags     = 0;
    int                 sigdigits = 0;
    int                 fdigits   = 0;
    int                 exponent  = 0;
    CHAR_TYPE           digitflag = '0';
    const CHAR_TYPE     *s = input;

    /* Parse the mantissa */
    for( ;; ) {
        chr = *s++;
        if( chr == '.' ) {  // <- needs changing for locale support
            if( flags & DOT_FOUND ) break;
            flags |= DOT_FOUND;
        } else {
            if( !isdigit( chr ) ) break;
            if( flags & DOT_FOUND ) {
                ++fdigits;
            }
            digitflag |= chr;
            if( digitflag != '0' ) {        /* if a significant digit */
                if( sigdigits < MAX_SIG_DIG ) {
                    buffer[ sigdigits ] = chr;
                }
                ++sigdigits;
            }
            flags |= DIGITS_PRESENT;
        }
    }

    /* Parse the optional exponent */
    if( flags & DIGITS_PRESENT ) {
        if( chr == 'e' || chr == 'E' ) {
            const CHAR_TYPE     *p = s - 1;

            chr = *s;
            if( chr == '+' ) {
                ++s;
            } else if( chr == '-' ) {
                flags |= NEGATIVE_EXPONENT;
                ++s;
            }
            flags &= ~DIGITS_PRESENT;
            for( ;; ) {
                chr = *s;                   /* don't increment s yet */
                if( !isdigit( chr ) ) break;
                if( exponent < 2000 ) {
                    exponent = exponent * 10 + chr - '0';
                }
                flags |= DIGITS_PRESENT;
                ++s;
            }
            if( flags & NEGATIVE_EXPONENT ) {
                exponent = -exponent;
            }
            if( !(flags & DIGITS_PRESENT) ) {
                s = p;
            }
        } else {    /* digits found, but no e or E */
            --s;
        }

        exponent -= fdigits;

        if( sigdigits > maxdigits ) {
            exponent += sigdigits - maxdigits;
            sigdigits = maxdigits;
        }

        while( sigdigits > 0 ) {
            if( buffer[ sigdigits - 1 ] != '0' ) break;
            ++exponent;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?