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 | ||
