summaryrefslogtreecommitdiff
path: root/mpf
diff options
context:
space:
mode:
Diffstat (limited to 'mpf')
-rw-r--r--mpf/set_str.c43
1 files changed, 38 insertions, 5 deletions
diff --git a/mpf/set_str.c b/mpf/set_str.c
index cb078de60..7e2becfd4 100644
--- a/mpf/set_str.c
+++ b/mpf/set_str.c
@@ -22,10 +22,16 @@ along with the GNU MP Library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA. */
+#include "config.h"
+
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#if HAVE_LOCALE_H
+#include <locale.h> /* for localeconv */
+#endif
+
#include "gmp.h"
#include "gmp-impl.h"
#include "longlong.h"
@@ -49,6 +55,21 @@ digit_value_in_base (int c, int base)
return -1;
}
+#if HAVE_LOCALECONV
+/* Avoid memcmp for the usual case that point is one character. Don't
+ bother with str+1,point+1,pointlen-1 since those offsets would add to the
+ code size. */
+#define POINTCMP_FAST(c, str) \
+ ((c) == point0 \
+ && (pointlen == 1 || memcmp (str, point, pointlen) == 0))
+#define POINTCMP_SMALL(c, str) (memcmp (str, point, pointlen) == 0)
+#define POINTLEN (pointlen)
+#else
+#define POINTCMP_FAST(c, str) ((c) == '.')
+#define POINTCMP_SMALL(c, str) ((c) == '.')
+#define POINTLEN 1
+#endif
+
int
mpf_set_str (mpf_ptr x, const char *str, int base)
{
@@ -61,6 +82,11 @@ mpf_set_str (mpf_ptr x, const char *str, int base)
char *dotpos = 0;
int expflag;
int decimal_exponent_flag;
+#if HAVE_LOCALECONV
+ const char *point = localeconv()->decimal_point;
+ size_t pointlen = strlen (point);
+ int point0 = point[0];
+#endif
TMP_DECL (marker);
TMP_MARK (marker);
@@ -81,9 +107,14 @@ mpf_set_str (mpf_ptr x, const char *str, int base)
decimal_exponent_flag = base < 0;
base = ABS (base);
- if (digit_value_in_base (c, base == 0 ? 10 : base) < 0
- && (c != '.' || digit_value_in_base (str[1], base == 0 ? 10 : base) < 0))
- return -1; /* error if no digits */
+ /* require at least one digit, possibly after an initial decimal point */
+ if (digit_value_in_base (c, base == 0 ? 10 : base) < 0)
+ {
+ if (! POINTCMP_SMALL (c, str))
+ return -1;
+ if (digit_value_in_base (str[POINTLEN], base == 0 ? 10 : base) < 0)
+ return -1;
+ }
/* If BASE is 0, try to find out the base by looking at the initial
characters. */
@@ -127,14 +158,16 @@ mpf_set_str (mpf_ptr x, const char *str, int base)
{
int dig;
- if (c == '.')
- {
+ if (POINTCMP_FAST (c, str))
+ {
if (dotpos != 0)
{
TMP_FREE (marker);
return -1;
}
dotpos = s;
+ str += POINTLEN - 1;
+ i += POINTLEN - 1;
}
else
{