aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMike Pall <mike>2025-12-08 22:18:40 +0100
committerMike Pall <mike>2025-12-08 22:18:40 +0100
commit7152e15489d2077cd299ee23e3d51a4c599ab14f (patch)
tree5edb2795f2bb6f274cf432d71e7382d8b05e04f7 /src
parent45b771bb2c693a4cc7e34e79b7d30ab10bb7776a (diff)
downloadluajit-7152e15489d2077cd299ee23e3d51a4c599ab14f.tar.gz
luajit-7152e15489d2077cd299ee23e3d51a4c599ab14f.tar.bz2
luajit-7152e15489d2077cd299ee23e3d51a4c599ab14f.zip
Fix string.format for limited precision FP conversions.
Enforce round-to-even semantics. #1363
Diffstat (limited to 'src')
-rw-r--r--src/lj_strfmt_num.c43
1 files changed, 40 insertions, 3 deletions
diff --git a/src/lj_strfmt_num.c b/src/lj_strfmt_num.c
index a33fc63a..35ff1bc9 100644
--- a/src/lj_strfmt_num.c
+++ b/src/lj_strfmt_num.c
@@ -169,7 +169,9 @@ static uint32_t nd_div2k(uint32_t* nd, uint32_t ndhi, uint32_t k, SFormat sf)
169 } 169 }
170 if (k > 18) { 170 if (k > 18) {
171 if (STRFMT_FP(sf) == STRFMT_FP(STRFMT_T_FP_F)) { 171 if (STRFMT_FP(sf) == STRFMT_FP(STRFMT_T_FP_F)) {
172 stop1 = 63 - (int32_t)STRFMT_PREC(sf) / 9; 172 /* Must not limit precision here or nd_round cannot round to even.
173 ** stop1 = 63 - (int32_t)STRFMT_PREC(sf) / 9;
174 */
173 } else { 175 } else {
174 int32_t floorlog2 = ndhi * 29 + lj_fls(nd[ndhi]) - k; 176 int32_t floorlog2 = ndhi * 29 + lj_fls(nd[ndhi]) - k;
175 int32_t floorlog10 = (int32_t)(floorlog2 * 0.30102999566398114); 177 int32_t floorlog10 = (int32_t)(floorlog2 * 0.30102999566398114);
@@ -242,6 +244,41 @@ static uint32_t nd_add_m10e(uint32_t* nd, uint32_t ndhi, uint8_t m, int32_t e)
242 return ndhi; 244 return ndhi;
243} 245}
244 246
247/* Round to even with given precision. Extra digits are not zeroed. */
248static uint32_t nd_round(uint32_t* nd, uint32_t ndlo, uint32_t ndhi, int32_t e)
249{
250 uint32_t i;
251 int32_t d;
252 char buf[9];
253 if (e >= 0) {
254 i = (uint32_t)e / 9;
255 d = 8 - e + (int32_t)i * 9;
256 } else {
257 int32_t f = (e - 8) / 9;
258 i = (uint32_t)(64 + f);
259 d = 8 - e + f * 9;
260 }
261 lj_strfmt_wuint9(buf, nd[i]);
262 if (buf[d] < '5') {
263 return ndhi; /* Don't round up. */
264 } else if (buf[d] == '5') { /* Must check for round to even. */
265 if (d ? (buf[d-1] & 1) : (nd[(i + 1) & 0x3f] & 1))
266 goto round_up; /* Round up '[13579]5.*' */
267 while (++d < 9) { /* Check remaining digits in buffer. */
268 if (buf[d] != '0')
269 goto round_up; /* Round up '[02468]5[^0]*'. */
270 }
271 while (i != ndlo) { /* Check remaining fraction. */
272 if (nd[i])
273 goto round_up; /* Round up '[02468]5[^0]*'. */
274 i = (i - 1) & 0x3f;
275 }
276 return ndhi; /* Don't round up. */
277 } /* else: round up.*/
278round_up:
279 return nd_add_m10e(nd, ndhi, 5, e); /* Round up by adding 5*10^e. */
280}
281
245/* Test whether two "nd" values are equal in their most significant digits. */ 282/* Test whether two "nd" values are equal in their most significant digits. */
246static int nd_similar(uint32_t* nd, uint32_t ndhi, uint32_t* ref, MSize hilen, 283static int nd_similar(uint32_t* nd, uint32_t ndhi, uint32_t* ref, MSize hilen,
247 MSize prec) 284 MSize prec)
@@ -432,7 +469,7 @@ static char *lj_strfmt_wfnum(SBuf *sb, SFormat sf, lua_Number n, char *p)
432 } 469 }
433 if ((int32_t)(prec - nde) < (0x3f & -(int32_t)ndlo) * 9) { 470 if ((int32_t)(prec - nde) < (0x3f & -(int32_t)ndlo) * 9) {
434 /* Precision is sufficiently low as to maybe require rounding. */ 471 /* Precision is sufficiently low as to maybe require rounding. */
435 ndhi = nd_add_m10e(nd, ndhi, 5, nde - prec - 1); 472 ndhi = nd_round(nd, ndlo, ndhi, nde - prec - 1);
436 nde += (hilen != ndigits_dec(nd[ndhi])); 473 nde += (hilen != ndigits_dec(nd[ndhi]));
437 } 474 }
438 nde += ndebias; 475 nde += ndebias;
@@ -508,7 +545,7 @@ static char *lj_strfmt_wfnum(SBuf *sb, SFormat sf, lua_Number n, char *p)
508 /* %f (or, shortly, %g in %f style) */ 545 /* %f (or, shortly, %g in %f style) */
509 if (prec < (MSize)(0x3f & -(int32_t)ndlo) * 9) { 546 if (prec < (MSize)(0x3f & -(int32_t)ndlo) * 9) {
510 /* Precision is sufficiently low as to maybe require rounding. */ 547 /* Precision is sufficiently low as to maybe require rounding. */
511 ndhi = nd_add_m10e(nd, ndhi, 5, 0 - prec - 1); 548 ndhi = nd_round(nd, ndlo, ndhi, 0 - prec - 1);
512 } 549 }
513 g_format_like_f: 550 g_format_like_f:
514 if ((sf & STRFMT_T_FP_E) && !(sf & STRFMT_F_ALT) && prec && width) { 551 if ((sf & STRFMT_T_FP_E) && !(sf & STRFMT_F_ALT) && prec && width) {