diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-08-02 15:09:30 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-08-02 15:09:30 -0300 |
commit | 1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a (patch) | |
tree | 5162b1f662c4dcb6127ba03866096f70d0c298ab /lobject.c | |
parent | 4c6afbcb01d1cae72d829af5301df5f592fa2079 (diff) | |
download | lua-1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a.tar.gz lua-1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a.tar.bz2 lua-1bf4b80f1ace8384eb9dd6f7f8b67256b3944a7a.zip |
Floats formatted with "correct" precision
Conversion float->string ensures that, for any float f,
tonumber(tostring(f)) == f, but still avoiding noise like 1.1
converting to "1.1000000000000001".
Diffstat (limited to 'lobject.c')
-rw-r--r-- | lobject.c | 52 |
1 files changed, 39 insertions, 13 deletions
@@ -10,6 +10,7 @@ | |||
10 | #include "lprefix.h" | 10 | #include "lprefix.h" |
11 | 11 | ||
12 | 12 | ||
13 | #include <float.h> | ||
13 | #include <locale.h> | 14 | #include <locale.h> |
14 | #include <math.h> | 15 | #include <math.h> |
15 | #include <stdarg.h> | 16 | #include <stdarg.h> |
@@ -401,29 +402,54 @@ int luaO_utf8esc (char *buff, unsigned long x) { | |||
401 | /* | 402 | /* |
402 | ** Maximum length of the conversion of a number to a string. Must be | 403 | ** Maximum length of the conversion of a number to a string. Must be |
403 | ** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. | 404 | ** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. |
404 | ** (For a long long int, this is 19 digits plus a sign and a final '\0', | 405 | ** For a long long int, this is 19 digits plus a sign and a final '\0', |
405 | ** adding to 21. For a long double, it can go to a sign, 33 digits, | 406 | ** adding to 21. For a long double, it can go to a sign, the dot, an |
406 | ** the dot, an exponent letter, an exponent sign, 5 exponent digits, | 407 | ** exponent letter, an exponent sign, 4 exponent digits, the final |
407 | ** and a final '\0', adding to 43.) | 408 | ** '\0', plus the significant digits, which are approximately the *_DIG |
409 | ** attribute. | ||
408 | */ | 410 | */ |
409 | #define MAXNUMBER2STR 44 | 411 | #define MAXNUMBER2STR (20 + l_floatatt(DIG)) |
410 | 412 | ||
411 | 413 | ||
412 | /* | 414 | /* |
413 | ** Convert a number object to a string, adding it to a buffer | 415 | ** Convert a float to a string, adding it to a buffer. First try with |
416 | ** a not too large number of digits, to avoid noise (for instance, | ||
417 | ** 1.1 going to "1.1000000000000001"). If that lose precision, so | ||
418 | ** that reading the result back gives a different number, then do the | ||
419 | ** conversion again with extra precision. Moreover, if the numeral looks | ||
420 | ** like an integer (without a decimal point or an exponent), add ".0" to | ||
421 | ** its end. | ||
422 | */ | ||
423 | static int tostringbuffFloat (lua_Number n, char *buff) { | ||
424 | /* first conversion */ | ||
425 | int len = l_sprintf(buff, MAXNUMBER2STR, LUA_NUMBER_FMT, | ||
426 | (LUAI_UACNUMBER)n); | ||
427 | lua_Number check = lua_str2number(buff, NULL); /* read it back */ | ||
428 | if (check != n) { /* not enough precision? */ | ||
429 | /* convert again with more precision */ | ||
430 | len = l_sprintf(buff, MAXNUMBER2STR, LUA_NUMBER_FMT_N, | ||
431 | (LUAI_UACNUMBER)n); | ||
432 | } | ||
433 | /* looks like an integer? */ | ||
434 | if (buff[strspn(buff, "-0123456789")] == '\0') { | ||
435 | buff[len++] = lua_getlocaledecpoint(); | ||
436 | buff[len++] = '0'; /* adds '.0' to result */ | ||
437 | } | ||
438 | return len; | ||
439 | } | ||
440 | |||
441 | |||
442 | /* | ||
443 | ** Convert a number object to a string, adding it to a buffer. | ||
414 | */ | 444 | */ |
415 | static unsigned tostringbuff (TValue *obj, char *buff) { | 445 | static unsigned tostringbuff (TValue *obj, char *buff) { |
416 | int len; | 446 | int len; |
417 | lua_assert(ttisnumber(obj)); | 447 | lua_assert(ttisnumber(obj)); |
418 | if (ttisinteger(obj)) | 448 | if (ttisinteger(obj)) |
419 | len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); | 449 | len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); |
420 | else { | 450 | else |
421 | len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); | 451 | len = tostringbuffFloat(fltvalue(obj), buff); |
422 | if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ | 452 | lua_assert(len < MAXNUMBER2STR); |
423 | buff[len++] = lua_getlocaledecpoint(); | ||
424 | buff[len++] = '0'; /* adds '.0' to result */ | ||
425 | } | ||
426 | } | ||
427 | return cast_uint(len); | 453 | return cast_uint(len); |
428 | } | 454 | } |
429 | 455 | ||