Skip to content

Commit

Permalink
Import locale insensitive strtod() from Ruby1.8; fix #3270
Browse files Browse the repository at this point in the history
The function was renamed to `mrb_float_read(const char*, char**)`.
  • Loading branch information
matz committed Dec 3, 2016
1 parent 802e396 commit a0fbc46
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 8 deletions.
5 changes: 2 additions & 3 deletions include/mruby/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ struct mrb_state;
# define MRB_PRIx PRIx32
#endif


MRB_API double mrb_float_read(const char*, char**);
#ifdef MRB_USE_FLOAT
typedef float mrb_float;
# define str_to_mrb_float(buf) strtof(buf, NULL)
#else
typedef double mrb_float;
# define str_to_mrb_float(buf) strtod(buf, NULL)
#endif

#if defined _MSC_VER && _MSC_VER < 1900
Expand All @@ -85,7 +85,6 @@ MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
# define isnan _isnan
# define isinf(n) (!_finite(n) && !_isnan(n))
# define signbit(n) (_copysign(1.0, (n)) < 0.0)
# define strtof (float)strtod
static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
# define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE)
# define NAN ((float)(INFINITY - INFINITY))
Expand Down
4 changes: 2 additions & 2 deletions mrbgems/mruby-compiler/core/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -2211,7 +2211,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_FLOAT:
if (val) {
char *p = (char*)tree;
mrb_float f = str_to_mrb_float(p);
mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, f));

genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
Expand All @@ -2227,7 +2227,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_FLOAT:
{
char *p = (char*)tree;
mrb_float f = str_to_mrb_float(p);
mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, -f));

genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
Expand Down
2 changes: 1 addition & 1 deletion mrbgems/mruby-compiler/core/parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -4912,7 +4912,7 @@ parser_yylex(parser_state *p)
char *endp;

errno = 0;
d = strtod(tok(p), &endp);
d = mrb_float_read(tok(p), &endp);
if (d == 0 && endp == tok(p)) {
yywarning_s(p, "corrupted float value %s", tok(p));
}
Expand Down
242 changes: 240 additions & 2 deletions src/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -2286,7 +2286,7 @@ mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
return 0.0;
}
d = strtod(p, &end);
d = mrb_float_read(p, &end);
if (p == end) {
if (badcheck) {
bad:
Expand Down Expand Up @@ -2324,7 +2324,7 @@ mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
return 0.0;
}

d = strtod(p, &end);
d = mrb_float_read(p, &end);
if (badcheck) {
if (!end || p == end) goto bad;
while (*end && ISSPACE(*end)) end++;
Expand Down Expand Up @@ -2749,3 +2749,241 @@ mrb_init_string(mrb_state *mrb)

mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE());
}

/*
* Source code for the "strtod" library procedure.
*
* Copyright (c) 1988-1993 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*
* RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $
*/

#include <ctype.h>
#include <errno.h>
extern int errno;

#ifndef __STDC__
# ifdef __GNUC__
# define const __const__
# else
# define const
# endif
#endif

static const int maxExponent = 511; /* Largest possible base 10 exponent. Any
* exponent larger than this will already
* produce underflow or overflow, so there's
* no need to worry about additional digits.
*/
static const double powersOf10[] = {/* Table giving binary powers of 10. Entry */
10., /* is 10^2^i. Used to convert decimal */
100., /* exponents into floating-point numbers. */
1.0e4,
1.0e8,
1.0e16,
1.0e32,
1.0e64,
1.0e128,
1.0e256
};

double
mrb_float_read(const char *string, char **endPtr)
/* const char *string; A decimal ASCII floating-point number,
* optionally preceded by white space.
* Must have form "-I.FE-X", where I is the
* integer part of the mantissa, F is the
* fractional part of the mantissa, and X
* is the exponent. Either of the signs
* may be "+", "-", or omitted. Either I
* or F may be omitted, or both. The decimal
* point isn't necessary unless F is present.
* The "E" may actually be an "e". E and X
* may both be omitted (but not just one).
*/
/* char **endPtr; If non-NULL, store terminating character's
* address here. */
{
int sign, expSign = FALSE;
double fraction, dblExp;
const double *d;
register const char *p;
register int c;
int exp = 0; /* Exponent read from "EX" field. */
int fracExp = 0; /* Exponent that derives from the fractional
* part. Under normal circumstatnces, it is
* the negative of the number of digits in F.
* However, if I is very long, the last digits
* of I get dropped (otherwise a long I with a
* large negative exponent could cause an
* unnecessary overflow on I alone). In this
* case, fracExp is incremented one for each
* dropped digit. */
int mantSize; /* Number of digits in mantissa. */
int decPt; /* Number of mantissa digits BEFORE decimal
* point. */
const char *pExp; /* Temporarily holds location of exponent
* in string. */

/*
* Strip off leading blanks and check for a sign.
*/

p = string;
while (isspace(*p)) {
p += 1;
}
if (*p == '-') {
sign = TRUE;
p += 1;
} else {
if (*p == '+') {
p += 1;
}
sign = FALSE;
}

/*
* Count the number of digits in the mantissa (including the decimal
* point), and also locate the decimal point.
*/

decPt = -1;
for (mantSize = 0; ; mantSize += 1)
{
c = *p;
if (!isdigit(c)) {
if ((c != '.') || (decPt >= 0)) {
break;
}
decPt = mantSize;
}
p += 1;
}

/*
* Now suck up the digits in the mantissa. Use two integers to
* collect 9 digits each (this is faster than using floating-point).
* If the mantissa has more than 18 digits, ignore the extras, since
* they can't affect the value anyway.
*/

pExp = p;
p -= mantSize;
if (decPt < 0) {
decPt = mantSize;
} else {
mantSize -= 1; /* One of the digits was the point. */
}
if (mantSize > 18) {
fracExp = decPt - 18;
mantSize = 18;
} else {
fracExp = decPt - mantSize;
}
if (mantSize == 0) {
fraction = 0.0;
p = string;
goto done;
} else {
int frac1, frac2;
frac1 = 0;
for ( ; mantSize > 9; mantSize -= 1)
{
c = *p;
p += 1;
if (c == '.') {
c = *p;
p += 1;
}
frac1 = 10*frac1 + (c - '0');
}
frac2 = 0;
for (; mantSize > 0; mantSize -= 1)
{
c = *p;
p += 1;
if (c == '.') {
c = *p;
p += 1;
}
frac2 = 10*frac2 + (c - '0');
}
fraction = (1.0e9 * frac1) + frac2;
}

/*
* Skim off the exponent.
*/

p = pExp;
if ((*p == 'E') || (*p == 'e')) {
p += 1;
if (*p == '-') {
expSign = TRUE;
p += 1;
} else {
if (*p == '+') {
p += 1;
}
expSign = FALSE;
}
while (isdigit(*p)) {
exp = exp * 10 + (*p - '0');
p += 1;
}
}
if (expSign) {
exp = fracExp - exp;
} else {
exp = fracExp + exp;
}

/*
* Generate a floating-point number that represents the exponent.
* Do this by processing the exponent one bit at a time to combine
* many powers of 2 of 10. Then combine the exponent with the
* fraction.
*/

if (exp < 0) {
expSign = TRUE;
exp = -exp;
} else {
expSign = FALSE;
}
if (exp > maxExponent) {
exp = maxExponent;
errno = ERANGE;
}
dblExp = 1.0;
for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
if (exp & 01) {
dblExp *= *d;
}
}
if (expSign) {
fraction /= dblExp;
} else {
fraction *= dblExp;
}

done:
if (endPtr != NULL) {
*endPtr = (char *) p;
}

if (sign) {
return -fraction;
}
return fraction;
}

0 comments on commit a0fbc46

Please sign in to comment.