From c92fb33b42bced81bbd8748dd5dd73916f33d1c4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 5 Nov 2004 03:21:24 +0000 Subject: [PATCH] Handle numbers like 1e1, nan, -infinity. Also try harder to preserve 2004-11-04 Matthias Clasen * glib/gstrfuncs.c (g_ascii_strtod): Handle numbers like 1e1, nan, -infinity. Also try harder to preserve errno. (#156421, Morten Welinder) * tests/strtod-test.c: Add testcases. --- ChangeLog | 8 ++++++ ChangeLog.pre-2-10 | 8 ++++++ ChangeLog.pre-2-12 | 8 ++++++ ChangeLog.pre-2-6 | 8 ++++++ ChangeLog.pre-2-8 | 8 ++++++ glib/gstrfuncs.c | 75 ++++++++++++++++++++++++++++------------------------- tests/strtod-test.c | 25 +++++++++++++++--- 7 files changed, 101 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index e000728..690d6ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2004-11-04 Matthias Clasen + + * glib/gstrfuncs.c (g_ascii_strtod): Handle numbers like + 1e1, nan, -infinity. Also try harder to preserve errno. + (#156421, Morten Welinder) + + * tests/strtod-test.c: Add testcases. + 2004-11-04 Tor Lillqvist * glib/goption.h (enum GOptionFlags): Add G_OPTION_FLAG_REVERSE, diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index e000728..690d6ae 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,11 @@ +2004-11-04 Matthias Clasen + + * glib/gstrfuncs.c (g_ascii_strtod): Handle numbers like + 1e1, nan, -infinity. Also try harder to preserve errno. + (#156421, Morten Welinder) + + * tests/strtod-test.c: Add testcases. + 2004-11-04 Tor Lillqvist * glib/goption.h (enum GOptionFlags): Add G_OPTION_FLAG_REVERSE, diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index e000728..690d6ae 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,11 @@ +2004-11-04 Matthias Clasen + + * glib/gstrfuncs.c (g_ascii_strtod): Handle numbers like + 1e1, nan, -infinity. Also try harder to preserve errno. + (#156421, Morten Welinder) + + * tests/strtod-test.c: Add testcases. + 2004-11-04 Tor Lillqvist * glib/goption.h (enum GOptionFlags): Add G_OPTION_FLAG_REVERSE, diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index e000728..690d6ae 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,11 @@ +2004-11-04 Matthias Clasen + + * glib/gstrfuncs.c (g_ascii_strtod): Handle numbers like + 1e1, nan, -infinity. Also try harder to preserve errno. + (#156421, Morten Welinder) + + * tests/strtod-test.c: Add testcases. + 2004-11-04 Tor Lillqvist * glib/goption.h (enum GOptionFlags): Add G_OPTION_FLAG_REVERSE, diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index e000728..690d6ae 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,11 @@ +2004-11-04 Matthias Clasen + + * glib/gstrfuncs.c (g_ascii_strtod): Handle numbers like + 1e1, nan, -infinity. Also try harder to preserve errno. + (#156421, Morten Welinder) + + * tests/strtod-test.c: Add testcases. + 2004-11-04 Tor Lillqvist * glib/goption.h (enum GOptionFlags): Add G_OPTION_FLAG_REVERSE, diff --git a/glib/gstrfuncs.c b/glib/gstrfuncs.c index a1f3123..afb0223 100644 --- a/glib/gstrfuncs.c +++ b/glib/gstrfuncs.c @@ -335,6 +335,7 @@ g_ascii_strtod (const gchar *nptr, int decimal_point_len; const char *p, *decimal_point_pos; const char *end = NULL; /* Silence gcc */ + int strtod_errno; g_return_val_if_fail (nptr != NULL, 0); @@ -347,6 +348,8 @@ g_ascii_strtod (const gchar *nptr, g_assert (decimal_point_len != 0); decimal_point_pos = NULL; + end = NULL; + if (decimal_point[0] != '.' || decimal_point[1] != 0) { @@ -369,48 +372,43 @@ g_ascii_strtod (const gchar *nptr, p++; if (*p == '.') - { - decimal_point_pos = p++; + decimal_point_pos = p++; - while (g_ascii_isxdigit (*p)) - p++; - - if (*p == 'p' || *p == 'P') - p++; - if (*p == '+' || *p == '-') - p++; - while (g_ascii_isdigit (*p)) - p++; - } + while (g_ascii_isxdigit (*p)) + p++; + + if (*p == 'p' || *p == 'P') + p++; + if (*p == '+' || *p == '-') + p++; + while (g_ascii_isdigit (*p)) + p++; + + end = p; } - else + else if (g_ascii_isdigit (*p)) { while (g_ascii_isdigit (*p)) p++; if (*p == '.') - { - decimal_point_pos = p++; - - while (g_ascii_isdigit (*p)) - p++; - - if (*p == 'e' || *p == 'E') - p++; - if (*p == '+' || *p == '-') - p++; - while (g_ascii_isdigit (*p)) - p++; - } + decimal_point_pos = p++; + + while (g_ascii_isdigit (*p)) + p++; + + if (*p == 'e' || *p == 'E') + p++; + if (*p == '+' || *p == '-') + p++; + while (g_ascii_isdigit (*p)) + p++; + + end = p; } /* For the other cases, we need not convert the decimal point */ - end = p; } - /* Set errno to zero, so that we can distinguish zero results - and underflows */ - errno = 0; - if (decimal_point_pos) { char *copy, *c; @@ -427,7 +425,9 @@ g_ascii_strtod (const gchar *nptr, c += end - (decimal_point_pos + 1); *c = 0; + errno = 0; val = strtod (copy, &fail_pos); + strtod_errno = errno; if (fail_pos) { @@ -440,8 +440,7 @@ g_ascii_strtod (const gchar *nptr, g_free (copy); } - else if (decimal_point[0] != '.' || - decimal_point[1] != 0) + else if (end) { char *copy; @@ -449,8 +448,10 @@ g_ascii_strtod (const gchar *nptr, memcpy (copy, nptr, end - nptr); *(copy + (end - (char *)nptr)) = 0; + errno = 0; val = strtod (copy, &fail_pos); - + strtod_errno = errno; + if (fail_pos) { fail_pos = (char *)nptr + (fail_pos - copy); @@ -460,12 +461,16 @@ g_ascii_strtod (const gchar *nptr, } else { + errno = 0; val = strtod (nptr, &fail_pos); + strtod_errno = errno; } if (endptr) *endptr = fail_pos; - + + errno = strtod_errno; + return val; } diff --git a/tests/strtod-test.c b/tests/strtod-test.c index 7e45570..3566ffe 100644 --- a/tests/strtod-test.c +++ b/tests/strtod-test.c @@ -1,15 +1,19 @@ #undef G_DISABLE_ASSERT #undef G_LOG_DOMAIN +/* for NAN and INFINITY */ +#define _ISOC99_SOURCE + #include #include #include +#include #include void test_string (char *number, double res, gboolean check_end, int correct_len) { - gdouble d; + double d; char *locales[] = {"sv_SE", "en_US", "fa_IR", "C", "ru_RU"}; int l; char *end; @@ -26,8 +30,16 @@ test_string (char *number, double res, gboolean check_end, int correct_len) { setlocale (LC_NUMERIC, locales[l]); d = g_ascii_strtod (number, &end); - if (d != res) - g_print ("g_ascii_strtod on \"%s\" for locale %s failed\n", number, locales[l]); + if ((isfinite(d) && isfinite(res) && d != res) || + (isnan (d) != isnan (res)) || + (isinf (d) != isinf (res))) + { + g_print ("g_ascii_strtod on \"%s\" for locale %s failed\n", number, locales[l]); + g_print ("expected %lf (nan %d inf %d) actual %lf (nan %d inf %d)\n", + res, isnan (res), isinf (res), + d, isnan (d), isinf (d)); + + } if (check_end && end - number != correct_len) g_print ("g_ascii_strtod on \"%s\" for locale %s endptr was wrong, leftover: %s\n", number, locales[l], end); if (!check_end && end - number != strlen (number)) @@ -50,10 +62,15 @@ main () test_string ("-123.123", -123.123, FALSE, 0); test_string ("-123.123e2", -123.123e2, FALSE, 0); test_string ("-123.123e-2", -123.123e-2, FALSE, 0); - test_string ("1e1", 1e1, FALSE, 0); test_string ("5.4", 5.4, TRUE, 3); test_string ("5.4,5.5", 5.4, TRUE, 3); test_string ("5,4", 5.0, TRUE, 1); + /* the following are for #156421 */ + test_string ("1e1", 1e1, FALSE, 0); + test_string ("NAN", NAN, FALSE, 0); + test_string ("-nan", -NAN, FALSE, 0); + test_string ("INF", INFINITY, FALSE, 0); + test_string ("-infinity", -INFINITY, FALSE, 0); d = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0; g_assert (d == g_ascii_strtod (g_ascii_dtostr (buffer, sizeof (buffer), d), NULL)); -- 2.7.4