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 + -
显示快捷键?