diff options
| author | Paul Eggert | 2011-04-19 23:24:51 -0700 |
|---|---|---|
| committer | Paul Eggert | 2011-04-19 23:24:51 -0700 |
| commit | 8b9587d73b579fb2fdd0eaaa1ed5fd608653e522 (patch) | |
| tree | 2f0c598b1d7bfe1e08fdf7b36fce973d7cbd657e /src | |
| parent | 602ea69dc7a93969742958ee6af3feae23cd1e02 (diff) | |
| download | emacs-8b9587d73b579fb2fdd0eaaa1ed5fd608653e522.tar.gz emacs-8b9587d73b579fb2fdd0eaaa1ed5fd608653e522.zip | |
Make the Lisp reader and string-to-float more consistent.
* data.c (atof): Remove decl; no longer used or needed.
(Fstring_to_number): Use new string_to_float function, to be
consistent with how the Lisp reader treats infinities and NaNs.
Do not assume that floating-point numbers represent EMACS_INT
without losing information; this is not true on most 64-bit hosts.
Avoid double-rounding errors, by insisting on integers when
parsing non-base-10 numbers, as the documentation specifies.
Report integer overflow instead of silently converting to
integers.
* lisp.h (string_to_float): New decl, replacing ...
(isfloat_string): Remove.
* lread.c (read1): Do not accept +. and -. as integers; this
appears to have been a coding error. Similarly, do not accept
strings like +-1e0 as floating point numbers. Do not report
overflow for some integer overflows and not others; instead,
report them all. Break out the floating-point parsing into a new
function string_to_float, so that Fstring_to_number parses
floating point numbers consistently with the Lisp reader.
(string_to_float): New function, replacing isfloat_string.
This function checks for valid syntax and produces the resulting
Lisp float number too.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 25 | ||||
| -rw-r--r-- | src/data.c | 42 | ||||
| -rw-r--r-- | src/lisp.h | 2 | ||||
| -rw-r--r-- | src/lread.c | 149 |
4 files changed, 106 insertions, 112 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 4a675cc96c6..c232c242fd7 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,28 @@ | |||
| 1 | 2011-04-20 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | Make the Lisp reader and string-to-float more consistent. | ||
| 4 | * data.c (atof): Remove decl; no longer used or needed. | ||
| 5 | (Fstring_to_number): Use new string_to_float function, to be | ||
| 6 | consistent with how the Lisp reader treats infinities and NaNs. | ||
| 7 | Do not assume that floating-point numbers represent EMACS_INT | ||
| 8 | without losing information; this is not true on most 64-bit hosts. | ||
| 9 | Avoid double-rounding errors, by insisting on integers when | ||
| 10 | parsing non-base-10 numbers, as the documentation specifies. | ||
| 11 | Report integer overflow instead of silently converting to | ||
| 12 | integers. | ||
| 13 | * lisp.h (string_to_float): New decl, replacing ... | ||
| 14 | (isfloat_string): Remove. | ||
| 15 | * lread.c (read1): Do not accept +. and -. as integers; this | ||
| 16 | appears to have been a coding error. Similarly, do not accept | ||
| 17 | strings like +-1e0 as floating point numbers. Do not report | ||
| 18 | overflow for some integer overflows and not others; instead, | ||
| 19 | report them all. Break out the floating-point parsing into a new | ||
| 20 | function string_to_float, so that Fstring_to_number parses | ||
| 21 | floating point numbers consistently with the Lisp reader. | ||
| 22 | (string_to_float): New function, replacing isfloat_string. | ||
| 23 | This function checks for valid syntax and produces the resulting | ||
| 24 | Lisp float number too. | ||
| 25 | |||
| 1 | 2011-04-19 Eli Zaretskii <eliz@gnu.org> | 26 | 2011-04-19 Eli Zaretskii <eliz@gnu.org> |
| 2 | 27 | ||
| 3 | * syntax.h (SETUP_SYNTAX_TABLE_FOR_OBJECT): Fix setting of | 28 | * syntax.h (SETUP_SYNTAX_TABLE_FOR_OBJECT): Fix setting of |
diff --git a/src/data.c b/src/data.c index c9250a67bf0..c3ee3e39939 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -48,10 +48,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 48 | 48 | ||
| 49 | #include <math.h> | 49 | #include <math.h> |
| 50 | 50 | ||
| 51 | #if !defined (atof) | ||
| 52 | extern double atof (const char *); | ||
| 53 | #endif /* !atof */ | ||
| 54 | |||
| 55 | Lisp_Object Qnil, Qt, Qquote, Qlambda, Qunbound; | 51 | Lisp_Object Qnil, Qt, Qquote, Qlambda, Qunbound; |
| 56 | static Lisp_Object Qsubr; | 52 | static Lisp_Object Qsubr; |
| 57 | Lisp_Object Qerror_conditions, Qerror_message, Qtop_level; | 53 | Lisp_Object Qerror_conditions, Qerror_message, Qtop_level; |
| @@ -2415,8 +2411,7 @@ If the base used is not 10, STRING is always parsed as integer. */) | |||
| 2415 | { | 2411 | { |
| 2416 | register char *p; | 2412 | register char *p; |
| 2417 | register int b; | 2413 | register int b; |
| 2418 | int sign = 1; | 2414 | EMACS_INT n; |
| 2419 | Lisp_Object val; | ||
| 2420 | 2415 | ||
| 2421 | CHECK_STRING (string); | 2416 | CHECK_STRING (string); |
| 2422 | 2417 | ||
| @@ -2430,38 +2425,23 @@ If the base used is not 10, STRING is always parsed as integer. */) | |||
| 2430 | xsignal1 (Qargs_out_of_range, base); | 2425 | xsignal1 (Qargs_out_of_range, base); |
| 2431 | } | 2426 | } |
| 2432 | 2427 | ||
| 2433 | /* Skip any whitespace at the front of the number. Some versions of | 2428 | /* Skip any whitespace at the front of the number. Typically strtol does |
| 2434 | atoi do this anyway, so we might as well make Emacs lisp consistent. */ | 2429 | this anyway, so we might as well be consistent. */ |
| 2435 | p = SSDATA (string); | 2430 | p = SSDATA (string); |
| 2436 | while (*p == ' ' || *p == '\t') | 2431 | while (*p == ' ' || *p == '\t') |
| 2437 | p++; | 2432 | p++; |
| 2438 | 2433 | ||
| 2439 | if (*p == '-') | 2434 | if (b == 10) |
| 2440 | { | ||
| 2441 | sign = -1; | ||
| 2442 | p++; | ||
| 2443 | } | ||
| 2444 | else if (*p == '+') | ||
| 2445 | p++; | ||
| 2446 | |||
| 2447 | if (isfloat_string (p, 1) && b == 10) | ||
| 2448 | val = make_float (sign * atof (p)); | ||
| 2449 | else | ||
| 2450 | { | 2435 | { |
| 2451 | double v = 0; | 2436 | Lisp_Object val = string_to_float (p, 1); |
| 2452 | 2437 | if (FLOATP (val)) | |
| 2453 | while (1) | 2438 | return val; |
| 2454 | { | ||
| 2455 | int digit = digit_to_number (*p++, b); | ||
| 2456 | if (digit < 0) | ||
| 2457 | break; | ||
| 2458 | v = v * b + digit; | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | val = make_fixnum_or_float (sign * v); | ||
| 2462 | } | 2439 | } |
| 2463 | 2440 | ||
| 2464 | return val; | 2441 | n = strtol (p, NULL, b); |
| 2442 | if (FIXNUM_OVERFLOW_P (n)) | ||
| 2443 | xsignal (Qoverflow_error, list1 (string)); | ||
| 2444 | return make_number (n); | ||
| 2465 | } | 2445 | } |
| 2466 | 2446 | ||
| 2467 | 2447 | ||
diff --git a/src/lisp.h b/src/lisp.h index 581835dd32b..6080007c780 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -2782,7 +2782,7 @@ extern Lisp_Object oblookup (Lisp_Object, const char *, EMACS_INT, EMACS_INT); | |||
| 2782 | } while (0) | 2782 | } while (0) |
| 2783 | extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, | 2783 | extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, |
| 2784 | Lisp_Object *, Lisp_Object); | 2784 | Lisp_Object *, Lisp_Object); |
| 2785 | extern int isfloat_string (const char *, int); | 2785 | Lisp_Object string_to_float (char const *, int); |
| 2786 | extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), | 2786 | extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), |
| 2787 | Lisp_Object); | 2787 | Lisp_Object); |
| 2788 | extern void dir_warning (const char *, Lisp_Object); | 2788 | extern void dir_warning (const char *, Lisp_Object); |
diff --git a/src/lread.c b/src/lread.c index d32f0b6a7e2..776d4ced7f2 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -3006,85 +3006,32 @@ read1 (register Lisp_Object readcharfun, int *pch, int first_in_list) | |||
| 3006 | if (!quoted && !uninterned_symbol) | 3006 | if (!quoted && !uninterned_symbol) |
| 3007 | { | 3007 | { |
| 3008 | register char *p1; | 3008 | register char *p1; |
| 3009 | Lisp_Object result; | ||
| 3009 | p1 = read_buffer; | 3010 | p1 = read_buffer; |
| 3010 | if (*p1 == '+' || *p1 == '-') p1++; | 3011 | if (*p1 == '+' || *p1 == '-') p1++; |
| 3011 | /* Is it an integer? */ | 3012 | /* Is it an integer? */ |
| 3012 | if (p1 != p) | 3013 | if ('0' <= *p1 && *p1 <= '9') |
| 3013 | { | 3014 | { |
| 3014 | while (p1 != p && (c = *p1) >= '0' && c <= '9') p1++; | 3015 | do |
| 3016 | p1++; | ||
| 3017 | while ('0' <= *p1 && *p1 <= '9'); | ||
| 3018 | |||
| 3015 | /* Integers can have trailing decimal points. */ | 3019 | /* Integers can have trailing decimal points. */ |
| 3016 | if (p1 > read_buffer && p1 < p && *p1 == '.') p1++; | 3020 | p1 += (*p1 == '.'); |
| 3017 | if (p1 == p) | 3021 | if (p1 == p) |
| 3018 | /* It is an integer. */ | ||
| 3019 | { | 3022 | { |
| 3020 | if (p1[-1] == '.') | 3023 | /* It is an integer. */ |
| 3021 | p1[-1] = '\0'; | 3024 | EMACS_INT n = strtol (read_buffer, NULL, 10); |
| 3022 | { | 3025 | if (FIXNUM_OVERFLOW_P (n)) |
| 3023 | /* EMACS_INT n = atol (read_buffer); */ | 3026 | xsignal (Qoverflow_error, |
| 3024 | char *endptr = NULL; | 3027 | list1 (build_string (read_buffer))); |
| 3025 | EMACS_INT n = (errno = 0, | 3028 | return make_number (n); |
| 3026 | strtol (read_buffer, &endptr, 10)); | ||
| 3027 | if (errno == ERANGE && endptr) | ||
| 3028 | { | ||
| 3029 | Lisp_Object args | ||
| 3030 | = Fcons (make_string (read_buffer, | ||
| 3031 | endptr - read_buffer), | ||
| 3032 | Qnil); | ||
| 3033 | xsignal (Qoverflow_error, args); | ||
| 3034 | } | ||
| 3035 | return make_fixnum_or_float (n); | ||
| 3036 | } | ||
| 3037 | } | 3029 | } |
| 3038 | } | 3030 | } |
| 3039 | if (isfloat_string (read_buffer, 0)) | ||
| 3040 | { | ||
| 3041 | /* Compute NaN and infinities using 0.0 in a variable, | ||
| 3042 | to cope with compilers that think they are smarter | ||
| 3043 | than we are. */ | ||
| 3044 | double zero = 0.0; | ||
| 3045 | |||
| 3046 | double value; | ||
| 3047 | |||
| 3048 | /* Negate the value ourselves. This treats 0, NaNs, | ||
| 3049 | and infinity properly on IEEE floating point hosts, | ||
| 3050 | and works around a common bug where atof ("-0.0") | ||
| 3051 | drops the sign. */ | ||
| 3052 | int negative = read_buffer[0] == '-'; | ||
| 3053 | |||
| 3054 | /* The only way p[-1] can be 'F' or 'N', after isfloat_string | ||
| 3055 | returns 1, is if the input ends in e+INF or e+NaN. */ | ||
| 3056 | switch (p[-1]) | ||
| 3057 | { | ||
| 3058 | case 'F': | ||
| 3059 | value = 1.0 / zero; | ||
| 3060 | break; | ||
| 3061 | case 'N': | ||
| 3062 | value = zero / zero; | ||
| 3063 | 3031 | ||
| 3064 | /* If that made a "negative" NaN, negate it. */ | 3032 | result = string_to_float (read_buffer, 0); |
| 3065 | 3033 | if (FLOATP (result)) | |
| 3066 | { | 3034 | return result; |
| 3067 | int i; | ||
| 3068 | union { double d; char c[sizeof (double)]; } u_data, u_minus_zero; | ||
| 3069 | |||
| 3070 | u_data.d = value; | ||
| 3071 | u_minus_zero.d = - 0.0; | ||
| 3072 | for (i = 0; i < sizeof (double); i++) | ||
| 3073 | if (u_data.c[i] & u_minus_zero.c[i]) | ||
| 3074 | { | ||
| 3075 | value = - value; | ||
| 3076 | break; | ||
| 3077 | } | ||
| 3078 | } | ||
| 3079 | /* Now VALUE is a positive NaN. */ | ||
| 3080 | break; | ||
| 3081 | default: | ||
| 3082 | value = atof (read_buffer + negative); | ||
| 3083 | break; | ||
| 3084 | } | ||
| 3085 | |||
| 3086 | return make_float (negative ? - value : value); | ||
| 3087 | } | ||
| 3088 | } | 3035 | } |
| 3089 | { | 3036 | { |
| 3090 | Lisp_Object name, result; | 3037 | Lisp_Object name, result; |
| @@ -3242,20 +3189,40 @@ substitute_in_interval (INTERVAL interval, Lisp_Object arg) | |||
| 3242 | } | 3189 | } |
| 3243 | 3190 | ||
| 3244 | 3191 | ||
| 3192 | /* Return the length of the floating-point number that is the prefix of CP, or | ||
| 3193 | zero if there is none. */ | ||
| 3194 | |||
| 3245 | #define LEAD_INT 1 | 3195 | #define LEAD_INT 1 |
| 3246 | #define DOT_CHAR 2 | 3196 | #define DOT_CHAR 2 |
| 3247 | #define TRAIL_INT 4 | 3197 | #define TRAIL_INT 4 |
| 3248 | #define E_CHAR 8 | 3198 | #define E_CHAR 8 |
| 3249 | #define EXP_INT 16 | 3199 | #define EXP_INT 16 |
| 3250 | 3200 | ||
| 3251 | int | 3201 | |
| 3252 | isfloat_string (const char *cp, int ignore_trailing) | 3202 | /* Convert CP to a floating point number. Return a non-float value if CP does |
| 3203 | not have valid floating point syntax. If IGNORE_TRAILING is nonzero, | ||
| 3204 | consider just the longest prefix of CP that has valid floating point | ||
| 3205 | syntax. */ | ||
| 3206 | |||
| 3207 | Lisp_Object | ||
| 3208 | string_to_float (char const *cp, int ignore_trailing) | ||
| 3253 | { | 3209 | { |
| 3254 | int state; | 3210 | int state; |
| 3255 | const char *start = cp; | 3211 | const char *start = cp; |
| 3256 | 3212 | ||
| 3213 | /* Compute NaN and infinities using a variable, to cope with compilers that | ||
| 3214 | think they are smarter than we are. */ | ||
| 3215 | double zero = 0; | ||
| 3216 | |||
| 3217 | /* Negate the value ourselves. This treats 0, NaNs, and infinity properly on | ||
| 3218 | IEEE floating point hosts, and works around a formerly-common bug where | ||
| 3219 | atof ("-0.0") drops the sign. */ | ||
| 3220 | int negative = *cp == '-'; | ||
| 3221 | |||
| 3222 | double value = 0; | ||
| 3223 | |||
| 3257 | state = 0; | 3224 | state = 0; |
| 3258 | if (*cp == '+' || *cp == '-') | 3225 | if (negative || *cp == '+') |
| 3259 | cp++; | 3226 | cp++; |
| 3260 | 3227 | ||
| 3261 | if (*cp >= '0' && *cp <= '9') | 3228 | if (*cp >= '0' && *cp <= '9') |
| @@ -3295,21 +3262,43 @@ isfloat_string (const char *cp, int ignore_trailing) | |||
| 3295 | { | 3262 | { |
| 3296 | state |= EXP_INT; | 3263 | state |= EXP_INT; |
| 3297 | cp += 3; | 3264 | cp += 3; |
| 3265 | value = 1.0 / zero; | ||
| 3298 | } | 3266 | } |
| 3299 | else if (cp[-1] == '+' && cp[0] == 'N' && cp[1] == 'a' && cp[2] == 'N') | 3267 | else if (cp[-1] == '+' && cp[0] == 'N' && cp[1] == 'a' && cp[2] == 'N') |
| 3300 | { | 3268 | { |
| 3301 | state |= EXP_INT; | 3269 | state |= EXP_INT; |
| 3302 | cp += 3; | 3270 | cp += 3; |
| 3271 | value = zero / zero; | ||
| 3272 | |||
| 3273 | /* If that made a "negative" NaN, negate it. */ | ||
| 3274 | { | ||
| 3275 | int i; | ||
| 3276 | union { double d; char c[sizeof (double)]; } u_data, u_minus_zero; | ||
| 3277 | |||
| 3278 | u_data.d = value; | ||
| 3279 | u_minus_zero.d = - 0.0; | ||
| 3280 | for (i = 0; i < sizeof (double); i++) | ||
| 3281 | if (u_data.c[i] & u_minus_zero.c[i]) | ||
| 3282 | { | ||
| 3283 | value = - value; | ||
| 3284 | break; | ||
| 3285 | } | ||
| 3286 | } | ||
| 3287 | /* Now VALUE is a positive NaN. */ | ||
| 3303 | } | 3288 | } |
| 3304 | 3289 | ||
| 3305 | return ((ignore_trailing | 3290 | if (! (state == (LEAD_INT|DOT_CHAR|TRAIL_INT) |
| 3306 | || *cp == 0 || *cp == ' ' || *cp == '\t' || *cp == '\n' | 3291 | || state == (DOT_CHAR|TRAIL_INT) |
| 3307 | || *cp == '\r' || *cp == '\f') | 3292 | || state == (LEAD_INT|E_CHAR|EXP_INT) |
| 3308 | && (state == (LEAD_INT|DOT_CHAR|TRAIL_INT) | 3293 | || state == (LEAD_INT|DOT_CHAR|TRAIL_INT|E_CHAR|EXP_INT) |
| 3309 | || state == (DOT_CHAR|TRAIL_INT) | 3294 | || state == (DOT_CHAR|TRAIL_INT|E_CHAR|EXP_INT))) |
| 3310 | || state == (LEAD_INT|E_CHAR|EXP_INT) | 3295 | return make_number (0); /* Any non-float value will do. */ |
| 3311 | || state == (LEAD_INT|DOT_CHAR|TRAIL_INT|E_CHAR|EXP_INT) | 3296 | |
| 3312 | || state == (DOT_CHAR|TRAIL_INT|E_CHAR|EXP_INT))); | 3297 | if (! value) |
| 3298 | value = atof (start + negative); | ||
| 3299 | if (negative) | ||
| 3300 | value = - value; | ||
| 3301 | return make_float (value); | ||
| 3313 | } | 3302 | } |
| 3314 | 3303 | ||
| 3315 | 3304 | ||