void attribute_hidden __fp_range_check(__fpmax_t y, __fpmax_t x) { if (__FPMAX_ZERO_OR_INF_CHECK(y) /* y is 0 or +/- infinity */ && (y != 0) /* y is not 0 (could have x>0, y==0 if underflow) */ && !__FPMAX_ZERO_OR_INF_CHECK(x) /* x is not 0 or +/- infinity */ ) { __set_errno(ERANGE); /* Then x is not in y's range. */ } }
size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, __fp_outfunc_t fp_outfunc) { #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ __fpmax_t lower_bnd; __fpmax_t upper_bnd = 1e9; #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ uint_fast32_t digit_block; #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ uint_fast32_t base = 10; const __fpmax_t *power_table; int dpb = DIGITS_PER_BLOCK; int ndb = NUM_DIGIT_BLOCKS; int nd = DECIMAL_DIG; int sufficient_precision = 0; #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ int num_groups = 0; int initial_group; /* This does not need to be initialized. */ int tslen; /* This does not need to be initialized. */ int nblk2; /* This does not need to be initialized. */ const char *ts; /* This does not need to be initialized. */ #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ int i, j; int round, o_exp; int exp, exp_neg; int width, preci; int cnt; char *s; char *e; intptr_t pc_fwi[3*MAX_CALLS]; intptr_t *ppc; intptr_t *ppc_last; #ifdef __UCLIBC_MJN3_ONLY__ #warning TODO: The size of exp_buf[] should really be determined by the float constants. #endif /* __UCLIBC_MJN3_ONLY__ */ char exp_buf[16]; char buf[BUF_SIZE]; char sign_str[6]; /* Last 2 are for 1st digit + nul. */ char o_mode; char mode; width = info->width; preci = info->prec; mode = info->spec; *exp_buf = 'e'; if ((mode|0x20) == 'a') { #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ *exp_buf = 'p'; if (preci < 0) { preci = NUM_HEX_DIGITS; sufficient_precision = 1; } #else mode += ('g' - 'a'); #endif } if (preci < 0) { preci = 6; } *sign_str = '\0'; if (PRINT_INFO_FLAG_VAL(info,showsign)) { *sign_str = '+'; } else if (PRINT_INFO_FLAG_VAL(info,space)) { *sign_str = ' '; } *(sign_str+1) = 0; pc_fwi[5] = INF_OFFSET; if (isnan(x)) { /* First, check for nan. */ pc_fwi[5] = NAN_OFFSET; goto INF_NAN; } if (x == 0) { /* Handle 0 now to avoid false positive. */ #if 1 if (zeroisnegative(x)) { /* Handle 'signed' zero. */ *sign_str = '-'; } #endif exp = -1; goto GENERATE_DIGITS; } if (x < 0) { /* Convert negatives to positives. */ *sign_str = '-'; x = -x; } if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */ INF_NAN: info->pad = ' '; ppc = pc_fwi + 6; pc_fwi[3] = FPO_STR_PREC; pc_fwi[4] = 3; if (mode < 'a') { pc_fwi[5] += 4; } pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]); goto EXIT_SPECIAL; } #ifdef __UCLIBC_MJN3_ONLY__ #warning TODO: Clean up defines when hexadecimal float notation is unsupported. #endif /* __UCLIBC_MJN3_ONLY__ */ #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ if ((mode|0x20) == 'a') { lower_bnd = 0x1.0p31L; upper_bnd = 0x1.0p32L; power_table = exp16_table; exp = HEX_DIGITS_PER_BLOCK - 1; i = EXP16_TABLE_SIZE; j = EXP16_TABLE_MAX; dpb = HEX_DIGITS_PER_BLOCK; ndb = NUM_HEX_DIGIT_BLOCKS; nd = NUM_HEX_DIGITS; base = 16; } else { lower_bnd = 1e8; /* upper_bnd = 1e9; */ power_table = exp10_table; exp = DIGITS_PER_BLOCK - 1; i = EXP10_TABLE_SIZE; j = EXP10_TABLE_MAX; /* dpb = DIGITS_PER_BLOCK; */ /* ndb = NUM_DIGIT_BLOCKS; */ /* base = 10; */ } #else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ #define lower_bnd 1e8 #define upper_bnd 1e9 #define power_table exp10_table #define dpb DIGITS_PER_BLOCK #define base 10 #define ndb NUM_DIGIT_BLOCKS #define nd DECIMAL_DIG exp = DIGITS_PER_BLOCK - 1; i = EXP10_TABLE_SIZE; j = EXP10_TABLE_MAX; #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ exp_neg = 0; if (x < lower_bnd) { /* Do we need to scale up or down? */ exp_neg = 1; } do { --i; if (exp_neg) { if (x * power_table[i] < upper_bnd) { x *= power_table[i]; exp -= j; } } else { if (x / power_table[i] >= lower_bnd) { x /= power_table[i]; exp += j; } } j >>= 1; } while (i); if (x >= upper_bnd) { /* Handle bad rounding case. */ x /= power_table[0]; ++exp; } assert(x < upper_bnd); GENERATE_DIGITS: s = buf + 2; /* Leave space for '\0' and '0'. */ i = 0; do { digit_block = (uint_fast32_t) x; assert(digit_block < upper_bnd); #ifdef __UCLIBC_MJN3_ONLY__ #warning CONSIDER: Can rounding be a problem? #endif /* __UCLIBC_MJN3_ONLY__ */ x = (x - digit_block) * upper_bnd; s += dpb; j = 0; do { s[- ++j] = '0' + (digit_block % base); digit_block /= base; } while (j < dpb); } while (++i < ndb); /*************************************************************************/ if (mode < 'a') { *exp_buf -= ('a' - 'A'); /* e->E and p->P */ mode += ('a' - 'A'); } o_mode = mode; if ((mode == 'g') && (preci > 0)){ --preci; } round = preci; if (mode == 'f') { round += exp; if (round < -1) { memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */ exp = -1; round = -1; } } s = buf; *s++ = 0; /* Terminator for rounding and 0-triming. */ *s = '0'; /* Space to round. */ i = 0; e = s + nd + 1; if (round < nd) { e = s + round + 2; if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */ i = 1; } } do { /* Handle rounding and trim trailing 0s. */ *--e += i; /* Add the carry. */ } while ((*e == '0') || (*e > '0' - 1 + base)); #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ if ((mode|0x20) == 'a') { char *q; for (q = e ; *q ; --q) { if (*q > '9') { *q += (*exp_buf - ('p' - 'a') - '9' - 1); } } if (e > s) { exp *= 4; /* Change from base 16 to base 2. */ } } #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ o_exp = exp; if (e <= s) { /* We carried into an extra digit. */ ++o_exp; e = s; /* Needed if all 0s. */ } else { ++s; } *++e = 0; /* Terminating nul char. */ if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { mode = 'f'; preci = round - o_exp; } exp = o_exp; if (mode != 'f') { o_exp = 0; } if (o_exp < 0) { /* Exponent is < 0, so */ *--s = '0'; /* fake the first 0 digit. */ } pc_fwi[3] = FPO_ZERO_PAD; pc_fwi[4] = 1; pc_fwi[5] = (intptr_t)(sign_str + 4); sign_str[4] = *s++; sign_str[5] = 0; ppc = pc_fwi + 6; i = e - s; /* Total digits is 'i'. */ if (o_exp >= 0) { #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ const char *p; if (PRINT_INFO_FLAG_VAL(info,group) && *(p = __UCLIBC_CURLOCALE_DATA.grouping) ) { int nblk1; nblk2 = nblk1 = *p; if (*++p) { nblk2 = *p; assert(!*++p); } if (o_exp >= nblk1) { num_groups = (o_exp - nblk1) / nblk2 + 1; initial_group = (o_exp - nblk1) % nblk2; #ifdef __UCLIBC_HAS_WCHAR__ if (PRINT_INFO_FLAG_VAL(info,wide)) { /* _fp_out_wide() will fix this up. */ ts = fmt + THOUSEP_OFFSET; tslen = 1; } else { #endif /* __UCLIBC_HAS_WCHAR__ */ ts = __UCLIBC_CURLOCALE_DATA.thousands_sep; tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len; #ifdef __UCLIBC_HAS_WCHAR__ } #endif /* __UCLIBC_HAS_WCHAR__ */ width -= num_groups * tslen; } } #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ ppc[0] = FPO_STR_PREC; ppc[2] = (intptr_t)(s); if (o_exp >= i) { /* all digit(s) left of decimal */ ppc[1] = i; ppc += 3; o_exp -= i; i = 0; if (o_exp>0) { /* have 0s left of decimal */ ppc[0] = FPO_ZERO_PAD; ppc[1] = o_exp; ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc += 3; } } else if (o_exp > 0) { /* decimal between digits */ ppc[1] = o_exp; ppc += 3; s += o_exp; i -= o_exp; } o_exp = -1; } if (PRINT_INFO_FLAG_VAL(info,alt) || (i) || ((o_mode != 'g') #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ && (o_mode != 'a') #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ && (preci > 0)) ) { ppc[0] = FPO_STR_PREC; #ifdef __LOCALE_C_ONLY ppc[1] = 1; ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); #else /* __LOCALE_C_ONLY */ #ifdef __UCLIBC_HAS_WCHAR__ if (PRINT_INFO_FLAG_VAL(info,wide)) { /* _fp_out_wide() will fix this up. */ ppc[1] = 1; ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); } else { #endif /* __UCLIBC_HAS_WCHAR__ */ ppc[1] = __UCLIBC_CURLOCALE_DATA.decimal_point_len; ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE_DATA.decimal_point); #ifdef __UCLIBC_HAS_WCHAR__ } #endif /* __UCLIBC_HAS_WCHAR__ */ #endif /* __LOCALE_C_ONLY */ ppc += 3; } if (++o_exp < 0) { /* Have 0s right of decimal. */ ppc[0] = FPO_ZERO_PAD; ppc[1] = -o_exp; ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc += 3; } if (i) { /* Have digit(s) right of decimal. */ ppc[0] = FPO_STR_PREC; ppc[1] = i; ppc[2] = (intptr_t)(s); ppc += 3; } if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt)) #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ && !sufficient_precision #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ ) { i -= o_exp; if (i < preci) { /* Have 0s right of digits. */ i = preci - i; ppc[0] = FPO_ZERO_PAD; ppc[1] = i; ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc += 3; } } /* Build exponent string. */ if (mode != 'f') { char *p = exp_buf + sizeof(exp_buf); char exp_char = *exp_buf; char exp_sign = '+'; #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1)); #else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ #define min_exp_dig_plus_2 (2+2) #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ if (exp < 0) { exp_sign = '-'; exp = -exp; } *--p = 0; /* nul-terminate */ j = 2; /* Count exp_char and exp_sign. */ do { *--p = '0' + (exp % 10); exp /= 10; } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */ *--p = exp_sign; *--p = exp_char; ppc[0] = FPO_STR_PREC; ppc[1] = j; ppc[2] = (intptr_t)(p); ppc += 3; } EXIT_SPECIAL: ppc_last = ppc; ppc = pc_fwi + 4; /* Need width fields starting with second. */ do { width -= *ppc; ppc += 3; } while (ppc < ppc_last); ppc = pc_fwi; ppc[0] = FPO_STR_WIDTH; ppc[1] = i = ((*sign_str) != 0); ppc[2] = (intptr_t) sign_str; #ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */ /* Hex and not inf or nan, so prefix with 0x. */ char *h = sign_str + i; *h = '0'; *++h = 'x' - 'p' + *exp_buf; *++h = 0; ppc[1] = (i += 2); } #endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ if ((width -= i) > 0) { if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */ ppc_last[0] = FPO_STR_WIDTH; ppc_last[1] = width; ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc_last += 3; } else if (info->pad == '0') { /* 0 padding */ ppc[4] += width; /* Pad second field. */ } else { ppc[1] += width; /* Pad first (sign) field. */ } } cnt = 0; do { #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ if ((ppc == pc_fwi + 6) && num_groups) { const char *gp = (const char *) ppc[2]; int len = ppc[1]; int blk = initial_group; cnt += num_groups * tslen; /* Adjust count now for sep chars. */ /* printf("\n"); */ do { if (!blk) { /* Initial group could be 0 digits long! */ blk = nblk2; } else if (len >= blk) { /* Enough digits for a group. */ /* printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */ fp_outfunc(fp, *ppc, blk, (intptr_t) gp); assert(gp); if (*gp) { gp += blk; } len -= blk; } else { /* Transition to 0s. */ /* printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */ if (len) { /* printf("len\n"); */ fp_outfunc(fp, *ppc, len, (intptr_t) gp); gp += len; } if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */ /* printf("zeropad\n"); */ cnt += ppc[1]; ppc += 3; gp = (const char *) ppc[2]; blk -= len; /* blk > len, so blk still > 0. */ len = ppc[1]; continue; /* Don't decrement num_groups here. */ } else { assert(num_groups == 0); break; } } if (num_groups <= 0) { break; } --num_groups; fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts); blk = nblk2; /* printf("num_groups=%d blk=%d\n", num_groups, blk); */ } while (1); } else #endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ fp_outfunc(fp, *ppc, ppc[1], ppc[2]); /* NOTE: Remember 'else' above! */ cnt += ppc[1]; ppc += 3; } while (ppc < ppc_last); return cnt; }
__fpmax_t attribute_hidden __XL_NPP(__strtofpmax)(const Wchar *str, Wchar **endptr, int exponent_power __LOCALE_PARAM ) { __fpmax_t number; __fpmax_t p_base = 10; /* Adjusted to 16 in the hex case. */ Wchar *pos0; #ifdef _STRTOD_ENDPTR Wchar *pos1; #endif Wchar *pos = (Wchar *) str; int exponent_temp; int negative; /* A flag for the number, a multiplier for the exponent. */ #ifdef _STRTOD_NEED_NUM_DIGITS int num_digits; #endif #ifdef __UCLIBC_HAS_LOCALE__ #if defined(L___wcstofpmax) || defined(L___wcstofpmax_l) wchar_t decpt_wc = __LOCALE_PTR->decimal_point_wc; #else const char *decpt = __LOCALE_PTR->decimal_point; int decpt_len = __LOCALE_PTR->decimal_point_len; #endif #endif #ifdef _STRTOD_HEXADECIMAL_FLOATS Wchar expchar = 'e'; Wchar *poshex = NULL; __uint16_t is_mask = _ISdigit; #define EXPCHAR expchar #define IS_X_DIGIT(C) __isctype((C), is_mask) #else /* _STRTOD_HEXADECIMAL_FLOATS */ #define EXPCHAR 'e' #define IS_X_DIGIT(C) isdigit((C)) #endif /* _STRTOD_HEXADECIMAL_FLOATS */ while (ISSPACE(*pos)) { /* Skip leading whitespace. */ ++pos; } negative = 0; switch(*pos) { /* Handle optional sign. */ case '-': negative = 1; /* Fall through to increment position. */ case '+': ++pos; } #ifdef _STRTOD_HEXADECIMAL_FLOATS if ((*pos == '0') && (((pos[1])|0x20) == 'x')) { poshex = ++pos; /* Save position of 'x' in case no digits */ ++pos; /* and advance past it. */ is_mask = _ISxdigit; /* Used by IS_X_DIGIT. */ expchar = 'p'; /* Adjust exponent char. */ p_base = 16; /* Adjust base multiplier. */ } #endif number = 0.; #ifdef _STRTOD_NEED_NUM_DIGITS num_digits = -1; #endif /* exponent_power = 0; */ pos0 = NULL; LOOP: while (IS_X_DIGIT(*pos)) { /* Process string of (hex) digits. */ #ifdef _STRTOD_RESTRICT_DIGITS if (num_digits < 0) { /* First time through? */ ++num_digits; /* We've now seen a digit. */ } if (num_digits || (*pos != '0')) { /* Had/have nonzero. */ ++num_digits; if (num_digits <= DECIMAL_DIG) { /* Is digit significant? */ #ifdef _STRTOD_HEXADECIMAL_FLOATS number = number * p_base + (isdigit(*pos) ? (*pos - '0') : (((*pos)|0x20) - ('a' - 10))); #else /* _STRTOD_HEXADECIMAL_FLOATS */ number = number * p_base + (*pos - '0'); #endif /* _STRTOD_HEXADECIMAL_FLOATS */ } } #else /* _STRTOD_RESTRICT_DIGITS */ #ifdef _STRTOD_NEED_NUM_DIGITS ++num_digits; #endif #ifdef _STRTOD_HEXADECIMAL_FLOATS number = number * p_base + (isdigit(*pos) ? (*pos - '0') : (((*pos)|0x20) - ('a' - 10))); #else /* _STRTOD_HEXADECIMAL_FLOATS */ number = number * p_base + (*pos - '0'); #endif /* _STRTOD_HEXADECIMAL_FLOATS */ #endif /* _STRTOD_RESTRICT_DIGITS */ ++pos; } #ifdef __UCLIBC_HAS_LOCALE__ #if defined(L___wcstofpmax) || defined(L___wcstofpmax_l) if (!pos0 && (*pos == decpt_wc)) { /* First decimal point? */ pos0 = ++pos; goto LOOP; } #else if (!pos0 && !memcmp(pos, decpt, decpt_len)) { /* First decimal point? */ pos0 = (pos += decpt_len); goto LOOP; } #endif #else /* __UCLIBC_HAS_LOCALE__ */ if ((*pos == '.') && !pos0) { /* First decimal point? */ pos0 = ++pos; /* Save position of decimal point */ goto LOOP; /* and process rest of digits. */ } #endif /* __UCLIBC_HAS_LOCALE__ */ #ifdef _STRTOD_NEED_NUM_DIGITS if (num_digits<0) { /* Must have at least one digit. */ #ifdef _STRTOD_HEXADECIMAL_FLOATS if (poshex) { /* Back up to '0' in '0x' prefix. */ pos = poshex; goto DONE; } #endif /* _STRTOD_HEXADECIMAL_FLOATS */ #ifdef _STRTOD_NAN_INF_STRINGS if (!pos0) { /* No decimal point, so check for inf/nan. */ /* Note: nan is the first string so 'number = i/0.;' works. */ static const char nan_inf_str[] = "\05nan\0\012infinity\0\05inf\0"; int i = 0; do { /* Unfortunately, we have no memcasecmp(). */ int j = 0; /* | 0x20 is a cheap lowercasing (valid for ASCII letters and numbers only) */ while ((pos[j] | 0x20) == nan_inf_str[i+1+j]) { ++j; if (!nan_inf_str[i+1+j]) { number = i / 0.; if (negative) { /* Correct for sign. */ number = -number; } pos += nan_inf_str[i] - 2; goto DONE; } } i += nan_inf_str[i]; } while (nan_inf_str[i]); } #endif /* STRTOD_NAN_INF_STRINGS */ #ifdef _STRTOD_ENDPTR pos = (Wchar *) str; #endif goto DONE; } #endif /* _STRTOD_NEED_NUM_DIGITS */ #ifdef _STRTOD_RESTRICT_DIGITS if (num_digits > DECIMAL_DIG) { /* Adjust exponent for skipped digits. */ exponent_power += num_digits - DECIMAL_DIG; } #endif if (pos0) { exponent_power += pos0 - pos; /* Adjust exponent for decimal point. */ } #ifdef _STRTOD_HEXADECIMAL_FLOATS if (poshex) { exponent_power *= 4; /* Above is 2**4, but below is 2. */ p_base = 2; } #endif /* _STRTOD_HEXADECIMAL_FLOATS */ if (negative) { /* Correct for sign. */ number = -number; } /* process an exponent string */ if (((*pos)|0x20) == EXPCHAR) { #ifdef _STRTOD_ENDPTR pos1 = pos; #endif negative = 1; switch(*++pos) { /* Handle optional sign. */ case '-': negative = -1; /* Fall through to increment pos. */ case '+': ++pos; } pos0 = pos; exponent_temp = 0; while (isdigit(*pos)) { /* Process string of digits. */ #ifdef _STRTOD_RESTRICT_EXP if (exponent_temp < MAX_ALLOWED_EXP) { /* Avoid overflow. */ exponent_temp = exponent_temp * 10 + (*pos - '0'); } #else exponent_temp = exponent_temp * 10 + (*pos - '0'); #endif ++pos; } #ifdef _STRTOD_ENDPTR if (pos == pos0) { /* No digits? */ pos = pos1; /* Back up to {e|E}/{p|P}. */ } /* else */ #endif exponent_power += negative * exponent_temp; } #ifdef _STRTOD_ZERO_CHECK if (number == 0.) { goto DONE; } #endif /* scale the result */ #ifdef _STRTOD_LOG_SCALING exponent_temp = exponent_power; if (exponent_temp < 0) { exponent_temp = -exponent_temp; } while (exponent_temp) { if (exponent_temp & 1) { if (exponent_power < 0) { /* Warning... caluclating a factor for the exponent and * then dividing could easily be faster. But doing so * might cause problems when dealing with denormals. */ number /= p_base; } else { number *= p_base; } } exponent_temp >>= 1; p_base *= p_base; } #else /* _STRTOD_LOG_SCALING */ while (exponent_power) { if (exponent_power < 0) { number /= p_base; exponent_power++; } else { number *= p_base; exponent_power--; } } #endif /* _STRTOD_LOG_SCALING */ #ifdef _STRTOD_ERRNO if (__FPMAX_ZERO_OR_INF_CHECK(number)) { __set_errno(ERANGE); } #endif DONE: #ifdef _STRTOD_ENDPTR if (endptr) { *endptr = pos; } #endif return number; }
int fptostr (__fpmax_t x, int width, int preci, char mode, char* buf, int maxlen) { __fpmax_t lower_bnd; __fpmax_t upper_bnd = 1e9; unsigned int base = 10; const __fpmax_t *power_table; int dpb = DIGITS_PER_BLOCK; int ndb = NUM_DIGIT_BLOCKS; int nd = DECIMAL_DIG; int sufficient_precision = 0; int round, o_exp; int exp; int cnt; char *s; char *e; typedef long int intptr_t; intptr_t pc_fwi[3*MAX_CALLS]; intptr_t *ppc; intptr_t *ppc_last; char exp_buf[16]; char sign_str[6]; /* Last 2 are for 1st digit + nul. */ char o_mode; char pad = ' '; char* orig_buf = buf; char temp_buf[BUF_SIZE]; *exp_buf = 'e'; if ((mode|0x20) == 'a') { *exp_buf = 'p'; if (preci < 0) { preci = NUM_HEX_DIGITS; sufficient_precision = 1; } } if (preci < 0) { preci = 6; } *sign_str = '\0'; *sign_str = '\0'; *(sign_str+1) = 0; pc_fwi[5] = INF_OFFSET; if (isnan(x)) { /* First, check for nan. */ pc_fwi[5] = NAN_OFFSET; goto INF_NAN; } if (x == 0) { /* Handle 0 now to avoid false positive. */ exp = -1; goto GENERATE_DIGITS; } if (x < 0) { /* Convert negatives to positives. */ *sign_str = '-'; x = -x; } if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */ INF_NAN: pad = ' '; ppc = pc_fwi + 6; pc_fwi[3] = FPO_STR_PREC; pc_fwi[4] = 3; if (mode < 'a') { pc_fwi[5] += 4; } pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]); goto EXIT_SPECIAL; } { int i, j; if ((mode|0x20) == 'a') { lower_bnd = 0x1.0p31L; upper_bnd = 0x1.0p32L; power_table = exp16_table; exp = HEX_DIGITS_PER_BLOCK - 1; i = EXP16_TABLE_SIZE; j = EXP16_TABLE_MAX; dpb = HEX_DIGITS_PER_BLOCK; ndb = NUM_HEX_DIGIT_BLOCKS; nd = NUM_HEX_DIGITS; base = 16; } else { lower_bnd = 1e8; /* upper_bnd = 1e9; */ power_table = exp10_table; exp = DIGITS_PER_BLOCK - 1; i = EXP10_TABLE_SIZE; j = EXP10_TABLE_MAX; /* dpb = DIGITS_PER_BLOCK; */ /* ndb = NUM_DIGIT_BLOCKS; */ /* base = 10; */ } { int exp_neg = 0; if (x < lower_bnd) { /* Do we need to scale up or down? */ exp_neg = 1; } do { --i; if (exp_neg) { if (x * power_table[i] < upper_bnd) { x *= power_table[i]; exp -= j; } } else { if (x / power_table[i] >= lower_bnd) { x /= power_table[i]; exp += j; } } j >>= 1; } while (i); } } while (x >= upper_bnd) { /* Handle bad rounding case. */ x /= power_table[0]; ++exp; } // RUDD - this may definitely be wrong // I couldn't get the assert to stop triggering // so I swapped the while to an if to continuously // round the number down. This seems reasonable and // I continue to pass the floating point regression tests // but this needs to be noted as a potential point of error. /* tl_assert(x <= upper_bnd); */ GENERATE_DIGITS: { int i, j; s = temp_buf + 2; /* Leave space for '\0' and '0'. */ i = 0; do { int digit_block = (int) x; tl_assert(digit_block < upper_bnd); x = (x - digit_block) * upper_bnd; s += dpb; j = 0; do { s[- ++j] = '0' + (digit_block % base); digit_block /= base; } while (j < dpb); } while (++i < ndb); } /*************************************************************************/ if (mode < 'a') { *exp_buf -= ('a' - 'A'); /* e->E and p->P */ mode += ('a' - 'A'); } o_mode = mode; if ((mode == 'g') && (preci > 0)){ --preci; } round = preci; if (mode == 'f') { round += exp; if (round < -1) { VG_(memset)(temp_buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */ exp = -1; round = -1; } } s = temp_buf; *s++ = 0; /* Terminator for rounding and 0-triming. */ *s = '0'; /* Space to round. */ { int i; i = 0; e = s + nd + 1; if (round < nd) { e = s + round + 2; if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */ i = 1; } } do { /* Handle rounding and trim trailing 0s. */ *--e += i; /* Add the carry. */ } while ((*e == '0') || (*e > '0' - 1 + base)); } if ((mode|0x20) == 'a') { char *q; for (q = e ; *q ; --q) { if (*q > '9') { *q += (*exp_buf - ('p' - 'a') - '9' - 1); } } if (e > s) { exp *= 4; /* Change from base 16 to base 2. */ } } o_exp = exp; if (e <= s) { /* We carried into an extra digit. */ ++o_exp; e = s; /* Needed if all 0s. */ } else { ++s; } *++e = 0; /* Terminating nul char. */ if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { mode = 'f'; preci = round - o_exp; } exp = o_exp; if (mode != 'f') { o_exp = 0; } if (o_exp < 0) { /* Exponent is < 0, so */ *--s = '0'; /* fake the first 0 digit. */ } pc_fwi[3] = FPO_ZERO_PAD; pc_fwi[4] = 1; pc_fwi[5] = (intptr_t)(sign_str + 4); sign_str[4] = *s++; sign_str[5] = 0; ppc = pc_fwi + 6; { int i = e - s; /* Total digits is 'i'. */ if (o_exp >= 0) { ppc[0] = FPO_STR_PREC; ppc[2] = (intptr_t)(s); if (o_exp >= i) { /* all digit(s) left of decimal */ ppc[1] = i; ppc += 3; o_exp -= i; i = 0; if (o_exp>0) { /* have 0s left of decimal */ ppc[0] = FPO_ZERO_PAD; ppc[1] = o_exp; ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc += 3; } } else if (o_exp > 0) { /* decimal between digits */ ppc[1] = o_exp; ppc += 3; s += o_exp; i -= o_exp; } o_exp = -1; } if ( (i) || ((o_mode != 'g') && (o_mode != 'a') && (preci > 0)) ) { ppc[0] = FPO_STR_PREC; ppc[1] = 1; ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); ppc += 3; } if (++o_exp < 0) { /* Have 0s right of decimal. */ ppc[0] = FPO_ZERO_PAD; ppc[1] = -o_exp; ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc += 3; } if (i) { /* Have digit(s) right of decimal. */ ppc[0] = FPO_STR_PREC; ppc[1] = i; ppc[2] = (intptr_t)(s); ppc += 3; } if (((o_mode != 'g')) && !sufficient_precision ) { i -= o_exp; if (i < preci) { /* Have 0s right of digits. */ i = preci - i; ppc[0] = FPO_ZERO_PAD; ppc[1] = i; ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); ppc += 3; } } } /* Build exponent string. */ if (mode != 'f') { char *p = exp_buf + sizeof(exp_buf); int j; char exp_char = *exp_buf; char exp_sign = '+'; int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1)); if (exp < 0) { exp_sign = '-'; exp = -exp; } *--p = 0; /* nul-terminate */ j = 2; /* Count exp_char and exp_sign. */ do { *--p = '0' + (exp % 10); exp /= 10; } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */ *--p = exp_sign; *--p = exp_char; ppc[0] = FPO_STR_PREC; ppc[1] = j; ppc[2] = (intptr_t)(p); ppc += 3; } EXIT_SPECIAL: { int i; ppc_last = ppc; ppc = pc_fwi + 4; /* Need width fields starting with second. */ do { width -= *ppc; ppc += 3; } while (ppc < ppc_last); ppc = pc_fwi; ppc[0] = FPO_STR_WIDTH; ppc[1] = i = ((*sign_str) != 0); ppc[2] = (intptr_t) sign_str; if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */ /* Hex and not inf or nan, so prefix with 0x. */ char *h = sign_str + i; *h = '0'; *++h = 'x' - 'p' + *exp_buf; *++h = 0; ppc[1] = (i += 2); } if ((width -= i) > 0) { if (pad == '0') { /* 0 padding */ ppc[4] += width; /* Pad second field. */ } else { ppc[1] += width; /* Pad first (sign) field. */ } } cnt = 0; } do { int i; int buflen = VG_(strlen)((const char*) ppc[2]); if (ppc[0] & 0x80) { if ((ppc[1] -= buflen) > 0) { for(i = 0; i < ppc[1]; i++) { if(ppc[0] & FPO_ZERO_PAD) { *buf++ = '0'; } else if(ppc[0] & FPO_STR_WIDTH) { *buf++ = ' '; } } } ppc[1] = buflen; } for(i=0; i < ppc[1]; i++) { *buf++ = *((char *)ppc[2] + i); } cnt += ppc[1]; ppc += 3; } while (ppc < ppc_last); *buf = '\0'; return VG_(strlen)(orig_buf); }