aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-03-04 23:14:52 -0800
committerPaul Eggert2017-03-04 23:18:39 -0800
commit44e7ee2e356452139156e8175c46f646835d27ff (patch)
tree7b877113c8ad6b9e2d64c560354656f7397b3325 /src
parent207de3303076bff1bb392bd407fee0dea892fe40 (diff)
downloademacs-44e7ee2e356452139156e8175c46f646835d27ff.tar.gz
emacs-44e7ee2e356452139156e8175c46f646835d27ff.zip
Fewer rounding errors with (format "%f" fixnum)
* etc/NEWS: Document this. * src/editfns.c (styled_format): When formatting integers via a floating-point format, use long double instead of double conversion, if long double’s extra precision might help.
Diffstat (limited to 'src')
-rw-r--r--src/editfns.c56
1 files changed, 43 insertions, 13 deletions
diff --git a/src/editfns.c b/src/editfns.c
index db9ad066909..612893c377b 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -4145,6 +4145,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4145 } 4145 }
4146 } 4146 }
4147 4147
4148 bool float_conversion
4149 = conversion == 'e' || conversion == 'f' || conversion == 'g';
4150
4148 if (conversion == 's') 4151 if (conversion == 's')
4149 { 4152 {
4150 /* handle case (precision[n] >= 0) */ 4153 /* handle case (precision[n] >= 0) */
@@ -4229,8 +4232,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4229 } 4232 }
4230 } 4233 }
4231 else if (! (conversion == 'c' || conversion == 'd' 4234 else if (! (conversion == 'c' || conversion == 'd'
4232 || conversion == 'e' || conversion == 'f' 4235 || float_conversion || conversion == 'i'
4233 || conversion == 'g' || conversion == 'i'
4234 || conversion == 'o' || conversion == 'x' 4236 || conversion == 'o' || conversion == 'x'
4235 || conversion == 'X')) 4237 || conversion == 'X'))
4236 error ("Invalid format operation %%%c", 4238 error ("Invalid format operation %%%c",
@@ -4242,11 +4244,22 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4242 { 4244 {
4243 enum 4245 enum
4244 { 4246 {
4247 /* Lower bound on the number of bits per
4248 base-FLT_RADIX digit. */
4249 DIG_BITS_LBOUND = FLT_RADIX < 16 ? 1 : 4,
4250
4251 /* 1 if integers should be formatted as long doubles,
4252 because they may be so large that there is a rounding
4253 error when converting them to double, and long doubles
4254 are wider than doubles. */
4255 INT_AS_LDBL = (DIG_BITS_LBOUND * DBL_MANT_DIG < FIXNUM_BITS - 1
4256 && DBL_MANT_DIG < LDBL_MANT_DIG),
4257
4245 /* Maximum precision for a %f conversion such that the 4258 /* Maximum precision for a %f conversion such that the
4246 trailing output digit might be nonzero. Any precision 4259 trailing output digit might be nonzero. Any precision
4247 larger than this will not yield useful information. */ 4260 larger than this will not yield useful information. */
4248 USEFUL_PRECISION_MAX = 4261 USEFUL_PRECISION_MAX =
4249 ((1 - DBL_MIN_EXP) 4262 ((1 - LDBL_MIN_EXP)
4250 * (FLT_RADIX == 2 || FLT_RADIX == 10 ? 1 4263 * (FLT_RADIX == 2 || FLT_RADIX == 10 ? 1
4251 : FLT_RADIX == 16 ? 4 4264 : FLT_RADIX == 16 ? 4
4252 : -1)), 4265 : -1)),
@@ -4255,7 +4268,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4255 precision is no more than USEFUL_PRECISION_MAX. 4268 precision is no more than USEFUL_PRECISION_MAX.
4256 On all practical hosts, %f is the worst case. */ 4269 On all practical hosts, %f is the worst case. */
4257 SPRINTF_BUFSIZE = 4270 SPRINTF_BUFSIZE =
4258 sizeof "-." + (DBL_MAX_10_EXP + 1) + USEFUL_PRECISION_MAX, 4271 sizeof "-." + (LDBL_MAX_10_EXP + 1) + USEFUL_PRECISION_MAX,
4259 4272
4260 /* Length of pM (that is, of pMd without the 4273 /* Length of pM (that is, of pMd without the
4261 trailing "d"). */ 4274 trailing "d"). */
@@ -4269,9 +4282,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4269 4282
4270 /* Create the copy of the conversion specification, with 4283 /* Create the copy of the conversion specification, with
4271 any width and precision removed, with ".*" inserted, 4284 any width and precision removed, with ".*" inserted,
4285 with "L" possibly inserted for floating-point formats,
4272 and with pM inserted for integer formats. 4286 and with pM inserted for integer formats.
4273 At most two flags F can be specified at once. */ 4287 At most two flags F can be specified at once. */
4274 char convspec[sizeof "%FF.*d" + pMlen]; 4288 char convspec[sizeof "%FF.*d" + max (INT_AS_LDBL, pMlen)];
4275 { 4289 {
4276 char *f = convspec; 4290 char *f = convspec;
4277 *f++ = '%'; 4291 *f++ = '%';
@@ -4281,9 +4295,15 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4281 *f = '#'; f += sharp_flag; 4295 *f = '#'; f += sharp_flag;
4282 *f++ = '.'; 4296 *f++ = '.';
4283 *f++ = '*'; 4297 *f++ = '*';
4284 if (conversion == 'd' || conversion == 'i' 4298 if (float_conversion)
4285 || conversion == 'o' || conversion == 'x' 4299 {
4286 || conversion == 'X') 4300 if (INT_AS_LDBL)
4301 {
4302 *f = 'L';
4303 f += INTEGERP (args[n]);
4304 }
4305 }
4306 else if (conversion != 'c')
4287 { 4307 {
4288 memcpy (f, pMd, pMlen); 4308 memcpy (f, pMd, pMlen);
4289 f += pMlen; 4309 f += pMlen;
@@ -4310,9 +4330,20 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4310 not suitable here. */ 4330 not suitable here. */
4311 char sprintf_buf[SPRINTF_BUFSIZE]; 4331 char sprintf_buf[SPRINTF_BUFSIZE];
4312 ptrdiff_t sprintf_bytes; 4332 ptrdiff_t sprintf_bytes;
4313 if (conversion == 'e' || conversion == 'f' || conversion == 'g') 4333 if (float_conversion)
4314 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, 4334 {
4315 XFLOATINT (args[n])); 4335 if (INT_AS_LDBL && INTEGERP (args[n]))
4336 {
4337 /* Although long double may have a rounding error if
4338 DIG_BITS_LBOUND * LDBL_MANT_DIG < FIXNUM_BITS - 1,
4339 it is more accurate than plain 'double'. */
4340 long double x = XINT (args[n]);
4341 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
4342 }
4343 else
4344 sprintf_bytes = sprintf (sprintf_buf, convspec, prec,
4345 XFLOATINT (args[n]));
4346 }
4316 else if (conversion == 'c') 4347 else if (conversion == 'c')
4317 { 4348 {
4318 /* Don't use sprintf here, as it might mishandle prec. */ 4349 /* Don't use sprintf here, as it might mishandle prec. */
@@ -4374,8 +4405,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4374 uintmax_t leading_zeros = 0, trailing_zeros = 0; 4405 uintmax_t leading_zeros = 0, trailing_zeros = 0;
4375 if (excess_precision) 4406 if (excess_precision)
4376 { 4407 {
4377 if (conversion == 'e' || conversion == 'f' 4408 if (float_conversion)
4378 || conversion == 'g')
4379 { 4409 {
4380 if ((conversion == 'g' && ! sharp_flag) 4410 if ((conversion == 'g' && ! sharp_flag)
4381 || ! ('0' <= sprintf_buf[sprintf_bytes - 1] 4411 || ! ('0' <= sprintf_buf[sprintf_bytes - 1]