aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2018-01-15 19:13:15 +0100
committerSverker Eriksson <[email protected]>2018-01-15 19:53:28 +0100
commitc81c01da7b4870aa56cc38d865cd081f9bb800bc (patch)
tree839e7351947b086907ccbe81199b43b2694c7cc2 /erts/emulator/sys
parenta8aeed346d0be42161a2563a09a4745b40d330ee (diff)
downloadotp-c81c01da7b4870aa56cc38d865cd081f9bb800bc.tar.gz
otp-c81c01da7b4870aa56cc38d865cd081f9bb800bc.tar.bz2
otp-c81c01da7b4870aa56cc38d865cd081f9bb800bc.zip
erts: Fix float_to_list(F, [{decimals,D}]).
Example symptom: 1> float_to_list(0.145, [{decimals,1}]). "0.2" There were two problems in sys_double_to_chars_fast 1. Most serious was adding 0.55555555 / (10^D) instead of 0.5 / (10^D) which imposed a 5.5% risk of a faulty rounding up. 2. Using fixpoint for frac_part which lost significant bits if F < 0.5
Diffstat (limited to 'erts/emulator/sys')
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c28
1 files changed, 21 insertions, 7 deletions
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 79f87eb3a9..ddb414580a 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -160,7 +160,7 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
{
/* Note that some C compilers don't support "static const" propagation
* so we use a defines */
- #define SYS_DOUBLE_RND_CONST 0.55555555555555555
+ #define SYS_DOUBLE_RND_CONST 0.5
#define FRAC_SIZE 52
#define EXP_SIZE 11
#define EXP_MASK ((1ll << EXP_SIZE) - 1)
@@ -256,11 +256,14 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
return p - buffer;
} else if (exp >= FRAC_SIZE) {
int_part = mantissa << (exp - FRAC_SIZE);
+ exp = 0;
} else if (exp >= 0) {
int_part = mantissa >> (FRAC_SIZE - exp);
frac_part = (mantissa << (exp + 1)) & FRAC_MASK2;
+ exp = 0;
} else /* if (exp < 0) */ {
- frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1);
+ frac_part = mantissa;
+ exp = -(exp+1);
}
if (!int_part) {
@@ -298,11 +301,22 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
max = decimals;
for (i = 0; i < max; i++) {
- /* frac_part *= 10; */
- frac_part = (frac_part << 3) + (frac_part << 1);
-
- *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
- frac_part &= FRAC_MASK2;
+ if (frac_part > (ERTS_UINT64_MAX/16)) {
+ frac_part /= 16;
+ exp -= 4;
+ }
+ if (FRAC_SIZE + 1 + exp >= 64) {
+ *p++ = '0';
+ frac_part *= 5;
+ exp--;
+ }
+ else
+ {
+ frac_part *= 10;
+
+ *p++ = (char)((frac_part >> (FRAC_SIZE + 1 + exp)) + '0');
+ frac_part &= (1ll << (FRAC_SIZE + 1 + exp)) - 1;
+ }
}
/* Delete trailing zeroes */