diff options
Diffstat (limited to 'gas/atof-generic.c')
-rw-r--r-- | gas/atof-generic.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/gas/atof-generic.c b/gas/atof-generic.c new file mode 100644 index 0000000..7d2d8f4 --- /dev/null +++ b/gas/atof-generic.c @@ -0,0 +1,549 @@ +/* atof_generic.c - turn a string of digits into a Flonum + Copyright (C) 1987, 1990, 1991 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GAS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include <ctype.h> +#include <string.h> + +#include "as.h" + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include <alloca.h> +#endif +#endif + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#endif + +/* #define FALSE (0) */ +/* #define TRUE (1) */ + +/***********************************************************************\ +* * +* Given a string of decimal digits , with optional decimal * +* mark and optional decimal exponent (place value) of the * +* lowest_order decimal digit: produce a floating point * +* number. The number is 'generic' floating point: our * +* caller will encode it for a specific machine architecture. * +* * +* Assumptions * +* uses base (radix) 2 * +* this machine uses 2's complement binary integers * +* target flonums use " " " " * +* target flonums exponents fit in a long * +* * +\***********************************************************************/ + +/* + + Syntax: + +<flonum> ::= <optional-sign> <decimal-number> <optional-exponent> +<optional-sign> ::= '+' | '-' | {empty} +<decimal-number> ::= <integer> + | <integer> <radix-character> + | <integer> <radix-character> <integer> + | <radix-character> <integer> +<optional-exponent> ::= {empty} | <exponent-character> <optional-sign> <integer> +<integer> ::= <digit> | <digit> <integer> +<digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' +<exponent-character> ::= {one character from "string_of_decimal_exponent_marks"} +<radix-character> ::= {one character from "string_of_decimal_marks"} + +*/ + +int /* 0 if OK */ +atof_generic ( + address_of_string_pointer, /* return pointer to just AFTER number we read. */ + string_of_decimal_marks, /* At most one per number. */ + string_of_decimal_exponent_marks, + address_of_generic_floating_point_number) + + char * * address_of_string_pointer; + const char * string_of_decimal_marks; + const char * string_of_decimal_exponent_marks; + FLONUM_TYPE * address_of_generic_floating_point_number; + +{ + + int return_value; /* 0 means OK. */ + char * first_digit; + /* char * last_digit; JF unused */ + int number_of_digits_before_decimal; + int number_of_digits_after_decimal; + long decimal_exponent; + int number_of_digits_available; + char digits_sign_char; + + { + /* + * Scan the input string, abstracting (1)digits (2)decimal mark (3) exponent. + * It would be simpler to modify the string, but we don't; just to be nice + * to caller. + * We need to know how many digits we have, so we can allocate space for + * the digits' value. + */ + + char * p; + char c; + int seen_significant_digit; + + first_digit = * address_of_string_pointer; + c= *first_digit; + if (c=='-' || c=='+') + { + digits_sign_char = c; + first_digit ++; + } + else + digits_sign_char = '+'; + + if( (first_digit[0]=='n' || first_digit[0]=='N') + && (first_digit[1]=='a' || first_digit[1]=='A') + && (first_digit[2]=='n' || first_digit[2]=='N')) { + address_of_generic_floating_point_number->sign=0; + address_of_generic_floating_point_number->exponent=0; + address_of_generic_floating_point_number->leader=address_of_generic_floating_point_number->low; + (*address_of_string_pointer)=first_digit+3; + return 0; + } + if( (first_digit[0]=='i' || first_digit[0]=='I') + && (first_digit[1]=='n' || first_digit[1]=='N') + && (first_digit[2]=='f' || first_digit[2]=='F')) { + address_of_generic_floating_point_number->sign= digits_sign_char=='+' ? 'P' : 'N'; + address_of_generic_floating_point_number->exponent=0; + address_of_generic_floating_point_number->leader=address_of_generic_floating_point_number->low; + if( (first_digit[3]=='i' || first_digit[3]=='I') + && (first_digit[4]=='n' || first_digit[4]=='N') + && (first_digit[5]=='i' || first_digit[5]=='I') + && (first_digit[6]=='t' || first_digit[6]=='T') + && (first_digit[7]=='y' || first_digit[7]=='Y')) + (*address_of_string_pointer)=first_digit+8; + else + (*address_of_string_pointer)=first_digit+3; + return 0; + } + + number_of_digits_before_decimal = 0; + number_of_digits_after_decimal = 0; + decimal_exponent = 0; + seen_significant_digit = 0; + for (p = first_digit; + ((c = * p) != '\0') + && (!c || ! strchr (string_of_decimal_marks, c) ) + && (!c || ! strchr (string_of_decimal_exponent_marks, c) ); + p ++) + { + if (isdigit(c)) + { + if (seen_significant_digit || c > '0') + { + number_of_digits_before_decimal ++; + seen_significant_digit = 1; + } + else + { + first_digit++; + } + } + else + { + break; /* p -> char after pre-decimal digits. */ + } + } /* For each digit before decimal mark. */ + +#ifndef OLD_FLOAT_READS + /* Ignore trailing 0's after the decimal point. The original code here + * (ifdef'd out) does not do this, and numbers like + * 4.29496729600000000000e+09 (2**31) + * come out inexact for some reason related to length of the digit + * string. + */ + if ( c && strchr(string_of_decimal_marks,c) ){ + int zeros = 0; /* Length of current string of zeros */ + + for ( p++; (c = *p) && isdigit(c); p++ ){ + if ( c == '0'){ + zeros++; + } else { + number_of_digits_after_decimal += 1 + zeros; + zeros = 0; + } + } + } +#else + if (c && strchr (string_of_decimal_marks, c)) + { + for (p ++; + ((c = * p) != '\0') + && (!c || ! strchr (string_of_decimal_exponent_marks, c) ); + p ++) + { + if (isdigit(c)) + { + number_of_digits_after_decimal ++; /* This may be retracted below. */ + if (/* seen_significant_digit || */ c > '0') + { + seen_significant_digit = TRUE; + } + } + else + { + if ( ! seen_significant_digit) + { + number_of_digits_after_decimal = 0; + } + break; + } + } /* For each digit after decimal mark. */ + } + while(number_of_digits_after_decimal && first_digit[number_of_digits_before_decimal+number_of_digits_after_decimal]=='0') + --number_of_digits_after_decimal; +/* last_digit = p; JF unused */ +#endif + + if (c && strchr (string_of_decimal_exponent_marks, c) ) + { + char digits_exponent_sign_char; + + c = * ++ p; + if (c && strchr ("+-",c)) + { + digits_exponent_sign_char = c; + c = * ++ p; + } + else + { + digits_exponent_sign_char = '+'; + } + for (; + (c); + c = * ++ p) + { + if (isdigit(c)) + { + decimal_exponent = decimal_exponent * 10 + c - '0'; + /* + * BUG! If we overflow here, we lose! + */ + } + else + { + break; + } + } + if (digits_exponent_sign_char == '-') + { + decimal_exponent = - decimal_exponent; + } + } + * address_of_string_pointer = p; + } + + number_of_digits_available = + number_of_digits_before_decimal + + number_of_digits_after_decimal; + return_value = 0; + if (number_of_digits_available == 0) + { + address_of_generic_floating_point_number -> exponent = 0; /* Not strictly necessary */ + address_of_generic_floating_point_number -> leader + = -1 + address_of_generic_floating_point_number -> low; + address_of_generic_floating_point_number -> sign = digits_sign_char; + /* We have just concocted (+/-)0.0E0 */ + } + else + { + LITTLENUM_TYPE * digits_binary_low; + int precision; + int maximum_useful_digits; + int number_of_digits_to_use; + int more_than_enough_bits_for_digits; + int more_than_enough_littlenums_for_digits; + int size_of_digits_in_littlenums; + int size_of_digits_in_chars; + FLONUM_TYPE power_of_10_flonum; + FLONUM_TYPE digits_flonum; + + + precision = (address_of_generic_floating_point_number -> high + - address_of_generic_floating_point_number -> low + + 1 + ); /* Number of destination littlenums. */ + /* Includes guard bits (two littlenums worth) */ + maximum_useful_digits = ( ((double) (precision - 2)) + * ((double) (LITTLENUM_NUMBER_OF_BITS)) + / (LOG_TO_BASE_2_OF_10) + ) + + 2; /* 2 :: guard digits. */ + if (number_of_digits_available > maximum_useful_digits) + { + number_of_digits_to_use = maximum_useful_digits; + } + else + { + number_of_digits_to_use = number_of_digits_available; + } + decimal_exponent += number_of_digits_before_decimal - number_of_digits_to_use; + + more_than_enough_bits_for_digits + = ((((double)number_of_digits_to_use) * LOG_TO_BASE_2_OF_10) + 1); + more_than_enough_littlenums_for_digits + = ( more_than_enough_bits_for_digits + / LITTLENUM_NUMBER_OF_BITS + ) + + 2; + + /* + * Compute (digits) part. In "12.34E56" this is the "1234" part. + * Arithmetic is exact here. If no digits are supplied then + * this part is a 0 valued binary integer. + * Allocate room to build up the binary number as littlenums. + * We want this memory to disappear when we leave this function. + * Assume no alignment problems => (room for n objects) == + * n * (room for 1 object). + */ + + size_of_digits_in_littlenums = more_than_enough_littlenums_for_digits; + size_of_digits_in_chars = size_of_digits_in_littlenums + * sizeof( LITTLENUM_TYPE ); + digits_binary_low = (LITTLENUM_TYPE *) + alloca (size_of_digits_in_chars); + bzero ((char *)digits_binary_low, size_of_digits_in_chars); + + /* Digits_binary_low[] is allocated and zeroed. */ + + { + /* + * Parse the decimal digits as if * digits_low was in the units position. + * Emit a binary number into digits_binary_low[]. + * + * Use a large-precision version of: + * (((1st-digit) * 10 + 2nd-digit) * 10 + 3rd-digit ...) * 10 + last-digit + */ + + char * p; + char c; + int count; /* Number of useful digits left to scan. */ + + for (p = first_digit, count = number_of_digits_to_use; + count; + p ++, -- count) + { + c = * p; + if (isdigit(c)) + { + /* + * Multiply by 10. Assume can never overflow. + * Add this digit to digits_binary_low[]. + */ + + long carry; + LITTLENUM_TYPE * littlenum_pointer; + LITTLENUM_TYPE * littlenum_limit; + + littlenum_limit + = digits_binary_low + + more_than_enough_littlenums_for_digits + - 1; + carry = c - '0'; /* char -> binary */ + for (littlenum_pointer = digits_binary_low; + littlenum_pointer <= littlenum_limit; + littlenum_pointer ++) + { + long work; + + work = carry + 10 * (long)(*littlenum_pointer); + * littlenum_pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry != 0) + { + /* + * We have a GROSS internal error. + * This should never happen. + */ + abort(); /* RMS prefers abort() to any message. */ + } + } + else + { + ++ count; /* '.' doesn't alter digits used count. */ + } /* if valid digit */ + } /* for each digit */ + } + + /* + * Digits_binary_low[] properly encodes the value of the digits. + * Forget about any high-order littlenums that are 0. + */ + while (digits_binary_low [size_of_digits_in_littlenums - 1] == 0 + && size_of_digits_in_littlenums >= 2) + size_of_digits_in_littlenums --; + + digits_flonum . low = digits_binary_low; + digits_flonum . high = digits_binary_low + size_of_digits_in_littlenums - 1; + digits_flonum . leader = digits_flonum . high; + digits_flonum . exponent = 0; + /* + * The value of digits_flonum . sign should not be important. + * We have already decided the output's sign. + * We trust that the sign won't influence the other parts of the number! + * So we give it a value for these reasons: + * (1) courtesy to humans reading/debugging + * these numbers so they don't get excited about strange values + * (2) in future there may be more meaning attached to sign, + * and what was + * harmless noise may become disruptive, ill-conditioned (or worse) + * input. + */ + digits_flonum . sign = '+'; + + { + /* + * Compute the mantssa (& exponent) of the power of 10. + * If sucessful, then multiply the power of 10 by the digits + * giving return_binary_mantissa and return_binary_exponent. + */ + + LITTLENUM_TYPE *power_binary_low; + int decimal_exponent_is_negative; + /* This refers to the "-56" in "12.34E-56". */ + /* FALSE: decimal_exponent is positive (or 0) */ + /* TRUE: decimal_exponent is negative */ + FLONUM_TYPE temporary_flonum; + LITTLENUM_TYPE *temporary_binary_low; + int size_of_power_in_littlenums; + int size_of_power_in_chars; + + size_of_power_in_littlenums = precision; +/* Precision has a built-in fudge factor so we get a few guard bits. */ + + + decimal_exponent_is_negative = decimal_exponent < 0; + if (decimal_exponent_is_negative) + { + decimal_exponent = - decimal_exponent; + } + /* From now on: the decimal exponent is > 0. Its sign is seperate. */ + + size_of_power_in_chars + = size_of_power_in_littlenums + * sizeof( LITTLENUM_TYPE ) + 2; + power_binary_low = (LITTLENUM_TYPE *) alloca ( size_of_power_in_chars ); + temporary_binary_low = (LITTLENUM_TYPE *) alloca ( size_of_power_in_chars ); + bzero ((char *)power_binary_low, size_of_power_in_chars); + * power_binary_low = 1; + power_of_10_flonum . exponent = 0; + power_of_10_flonum . low = power_binary_low; + power_of_10_flonum . leader = power_binary_low; + power_of_10_flonum . high = power_binary_low + size_of_power_in_littlenums - 1; + power_of_10_flonum . sign = '+'; + temporary_flonum . low = temporary_binary_low; + temporary_flonum . high = temporary_binary_low + size_of_power_in_littlenums - 1; + /* + * (power) == 1. + * Space for temporary_flonum allocated. + */ + + /* + * ... + * + * WHILE more bits + * DO find next bit (with place value) + * multiply into power mantissa + * OD + */ + { + int place_number_limit; + /* Any 10^(2^n) whose "n" exceeds this */ + /* value will fall off the end of */ + /* flonum_XXXX_powers_of_ten[]. */ + int place_number; + const FLONUM_TYPE * multiplicand; /* -> 10^(2^n) */ + + place_number_limit = table_size_of_flonum_powers_of_ten; + multiplicand + = ( decimal_exponent_is_negative + ? flonum_negative_powers_of_ten + : flonum_positive_powers_of_ten); + for (place_number = 1; /* Place value of this bit of exponent. */ + decimal_exponent; /* Quit when no more 1 bits in exponent. */ + decimal_exponent >>= 1 + , place_number ++) + { + if (decimal_exponent & 1) + { + if (place_number > place_number_limit) + { + /* + * The decimal exponent has a magnitude so great that + * our tables can't help us fragment it. Although this + * routine is in error because it can't imagine a + * number that big, signal an error as if it is the + * user's fault for presenting such a big number. + */ + return_value = ERROR_EXPONENT_OVERFLOW; + /* + * quit out of loop gracefully + */ + decimal_exponent = 0; + } + else + { +#ifdef TRACE +printf("before multiply, place_number = %d., power_of_10_flonum:\n", place_number); +flonum_print( & power_of_10_flonum ); +(void)putchar('\n'); +#endif + flonum_multip(multiplicand + place_number, &power_of_10_flonum, &temporary_flonum); + flonum_copy (& temporary_flonum, & power_of_10_flonum); + } /* If this bit of decimal_exponent was computable.*/ + } /* If this bit of decimal_exponent was set. */ + } /* For each bit of binary representation of exponent */ +#ifdef TRACE +printf( " after computing power_of_10_flonum: " ); +flonum_print( & power_of_10_flonum ); +(void)putchar('\n'); +#endif + } + + } + + /* + * power_of_10_flonum is power of ten in binary (mantissa) , (exponent). + * It may be the number 1, in which case we don't NEED to multiply. + * + * Multiply (decimal digits) by power_of_10_flonum. + */ + + flonum_multip (& power_of_10_flonum, & digits_flonum, address_of_generic_floating_point_number); + /* Assert sign of the number we made is '+'. */ + address_of_generic_floating_point_number -> sign = digits_sign_char; + + } /* If we had any significant digits. */ + return (return_value); +} /* atof_generic () */ + +/* end: atof_generic.c */ |