File 0249-erts-Fix-float_to_list-F-decimals-D.patch of Package erlang

From c81c01da7b4870aa56cc38d865cd081f9bb800bc Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Mon, 15 Jan 2018 19:13:15 +0100
Subject: [PATCH 2/3] 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
---
 erts/emulator/sys/common/erl_sys_common_misc.c | 28 +++++++---
 erts/emulator/test/num_bif_SUITE.erl           | 71 +++++++++++++++++++++++---
 2 files changed, 84 insertions(+), 15 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 */
-- 
2.16.0

openSUSE Build Service is sponsored by