diff mbox series

[2/4] libstdc++: Apply modifications to our local copy of Ryu

Message ID 20200714194146.3978209-2-ppalka@redhat.com
State New
Headers show
Series None | expand

Commit Message

Patrick Palka July 14, 2020, 7:41 p.m. UTC
This performs the following modifications to our local copy of Ryu in
order to make it more easily usable for our std::to_chars implementation:

  * Remove all #includes
  * Remove copy_special_str routines
  * Adjust the exponent formatting to match printf
  * Remove some functions we're not going to use
  * Add an out-parameter to d2exp_buffered_n for the scientific exponent
  * Store the sign bit inside struct floating_decimal_[32|64]
  * Rename [df]2s_buffered_n and change their return type

libstdc++-v3/ChangeLog:

	* src/c++17/ryu/common.h, src/c++17/ryu/d2fixed.c,
	src/c++17/ryu/d2fixed_full_table.h, src/c++17/ryu/d2s.c,
	src/c++17/ryu/d2s_intrinsics.h, src/c++17/ryu/f2s.c,
	src/c++17/ryu/f2s_intrinsics.h: Apply local modifications.
---
 libstdc++-v3/src/c++17/ryu/common.h           | 19 ----
 libstdc++-v3/src/c++17/ryu/d2fixed.c          | 98 ++-----------------
 .../src/c++17/ryu/d2fixed_full_table.h        |  1 -
 libstdc++-v3/src/c++17/ryu/d2s.c              | 56 +++--------
 libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h   |  4 -
 libstdc++-v3/src/c++17/ryu/f2s.c              | 52 +++-------
 libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h   |  4 -
 libstdc++-v3/src/c++17/ryu/generic_128.c      |  7 +-
 8 files changed, 43 insertions(+), 198 deletions(-)

Comments

Patrick Palka July 15, 2020, 6:12 p.m. UTC | #1
On Tue, 14 Jul 2020, Patrick Palka wrote:

> This performs the following modifications to our local copy of Ryu in
> order to make it more easily usable for our std::to_chars implementation:
> 
>   * Remove all #includes
>   * Remove copy_special_str routines
>   * Adjust the exponent formatting to match printf
>   * Remove some functions we're not going to use
>   * Add an out-parameter to d2exp_buffered_n for the scientific exponent
>   * Store the sign bit inside struct floating_decimal_[32|64]
>   * Rename [df]2s_buffered_n and change their return type

Here is v2 of this patch, which applies another modification to Ryu that 
v2 of the third patch in the series depends on:

-- >8 --

Subject: [PATCH 2/4] libstdc++: Apply modifications to our local copy of Ryu

This performs the following modifications to our local copy of Ryu in
order to make it more easily usable in our std::to_chars implementation:

  * Remove all #includes
  * Remove copy_special_str routines
  * Adjust the exponent formatting to match printf
  * Remove some functions we're not going to use
  * Add an out-parameter to d2exp_buffered_n for the scientific exponent
  * Store the sign bit inside struct floating_decimal_[32|64]
  * Rename [df]2s_buffered_n and change their return type
  * Make generic_binary_to_decimal take the bit representation in parts

libstdc++-v3/ChangeLog:

	* src/c++17/ryu/common.h, src/c++17/ryu/d2fixed.c,
	src/c++17/ryu/d2fixed_full_table.h, src/c++17/ryu/d2s.c,
	src/c++17/ryu/d2s_intrinsics.h, src/c++17/ryu/f2s.c,
	src/c++17/ryu/f2s_intrinsics.h: Apply local modifications.
---
 libstdc++-v3/src/c++17/ryu/common.h           | 19 ----
 libstdc++-v3/src/c++17/ryu/d2fixed.c          | 98 ++-----------------
 .../src/c++17/ryu/d2fixed_full_table.h        |  1 -
 libstdc++-v3/src/c++17/ryu/d2s.c              | 56 +++--------
 libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h   |  4 -
 libstdc++-v3/src/c++17/ryu/f2s.c              | 52 +++-------
 libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h   |  4 -
 libstdc++-v3/src/c++17/ryu/generic_128.c      | 47 ++-------
 8 files changed, 45 insertions(+), 236 deletions(-)

diff --git a/libstdc++-v3/src/c++17/ryu/common.h b/libstdc++-v3/src/c++17/ryu/common.h
index 7dc130947ac..f8ee147db04 100644
--- a/libstdc++-v3/src/c++17/ryu/common.h
+++ b/libstdc++-v3/src/c++17/ryu/common.h
@@ -17,9 +17,6 @@
 #ifndef RYU_COMMON_H
 #define RYU_COMMON_H
 
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
 
 #if defined(_M_IX86) || defined(_M_ARM)
 #define RYU_32_BIT_PLATFORM
@@ -83,22 +80,6 @@ static inline uint32_t log10Pow5(const int32_t e) {
   return (((uint32_t) e) * 732923) >> 20;
 }
 
-static inline int copy_special_str(char * const result, const bool sign, const bool exponent, const bool mantissa) {
-  if (mantissa) {
-    memcpy(result, "NaN", 3);
-    return 3;
-  }
-  if (sign) {
-    result[0] = '-';
-  }
-  if (exponent) {
-    memcpy(result + sign, "Infinity", 8);
-    return sign + 8;
-  }
-  memcpy(result + sign, "0E0", 3);
-  return sign + 3;
-}
-
 static inline uint32_t float_to_bits(const float f) {
   uint32_t bits = 0;
   memcpy(&bits, &f, sizeof(float));
diff --git a/libstdc++-v3/src/c++17/ryu/d2fixed.c b/libstdc++-v3/src/c++17/ryu/d2fixed.c
index 5f479abb91b..642a29d3010 100644
--- a/libstdc++-v3/src/c++17/ryu/d2fixed.c
+++ b/libstdc++-v3/src/c++17/ryu/d2fixed.c
@@ -23,23 +23,11 @@
 //
 // -DRYU_AVOID_UINT128 Avoid using uint128_t. Slower, depending on your compiler.
 
-#include "ryu/ryu.h"
 
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
 
 #ifdef RYU_DEBUG
-#include <inttypes.h>
-#include <stdio.h>
 #endif
 
-#include "ryu/common.h"
-#include "ryu/digit_table.h"
-#include "ryu/d2fixed_full_table.h"
-#include "ryu/d2s_intrinsics.h"
 
 #define DOUBLE_MANTISSA_BITS 52
 #define DOUBLE_EXPONENT_BITS 11
@@ -328,33 +316,6 @@ static inline uint32_t lengthForIndex(const uint32_t idx) {
   return (log10Pow2(16 * (int32_t) idx) + 1 + 16 + 8) / 9;
 }
 
-static inline int copy_special_str_printf(char* const result, const bool sign, const uint64_t mantissa) {
-#if defined(_MSC_VER)
-  // TODO: Check that -nan is expected output on Windows.
-  if (sign) {
-    result[0] = '-';
-  }
-  if (mantissa) {
-    if (mantissa < (1ull << (DOUBLE_MANTISSA_BITS - 1))) {
-      memcpy(result + sign, "nan(snan)", 9);
-      return sign + 9;
-    }
-    memcpy(result + sign, "nan", 3);
-    return sign + 3;
-  }
-#else
-  if (mantissa) {
-    memcpy(result, "nan", 3);
-    return 3;
-  }
-  if (sign) {
-    result[0] = '-';
-  }
-#endif
-  memcpy(result + sign, "Infinity", 8);
-  return sign + 8;
-}
-
 int d2fixed_buffered_n(double d, uint32_t precision, char* result) {
   const uint64_t bits = double_to_bits(d);
 #ifdef RYU_DEBUG
@@ -372,20 +333,10 @@ int d2fixed_buffered_n(double d, uint32_t precision, char* result) {
 
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u)) {
-    return copy_special_str_printf(result, ieeeSign, ieeeMantissa);
+    __builtin_abort();
   }
   if (ieeeExponent == 0 && ieeeMantissa == 0) {
-    int index = 0;
-    if (ieeeSign) {
-      result[index++] = '-';
-    }
-    result[index++] = '0';
-    if (precision > 0) {
-      result[index++] = '.';
-      memset(result + index, '0', precision);
-      index += precision;
-    }
-    return index;
+    __builtin_abort();
   }
 
   int32_t e2;
@@ -549,21 +500,9 @@ int d2fixed_buffered_n(double d, uint32_t precision, char* result) {
   return index;
 }
 
-void d2fixed_buffered(double d, uint32_t precision, char* result) {
-  const int len = d2fixed_buffered_n(d, precision, result);
-  result[len] = '\0';
-}
-
-char* d2fixed(double d, uint32_t precision) {
-  char* const buffer = (char*)malloc(2000);
-  const int index = d2fixed_buffered_n(d, precision, buffer);
-  buffer[index] = '\0';
-  return buffer;
-}
-
 
 
-int d2exp_buffered_n(double d, uint32_t precision, char* result) {
+int d2exp_buffered_n(double d, uint32_t precision, char* result, int* exp_out) {
   const uint64_t bits = double_to_bits(d);
 #ifdef RYU_DEBUG
   printf("IN=");
@@ -580,22 +519,10 @@ int d2exp_buffered_n(double d, uint32_t precision, char* result) {
 
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u)) {
-    return copy_special_str_printf(result, ieeeSign, ieeeMantissa);
+    __builtin_abort();
   }
   if (ieeeExponent == 0 && ieeeMantissa == 0) {
-    int index = 0;
-    if (ieeeSign) {
-      result[index++] = '-';
-    }
-    result[index++] = '0';
-    if (precision > 0) {
-      result[index++] = '.';
-      memset(result + index, '0', precision);
-      index += precision;
-    }
-    memcpy(result + index, "e+00", 4);
-    index += 4;
-    return index;
+    __builtin_abort();
   }
 
   int32_t e2;
@@ -785,6 +712,9 @@ int d2exp_buffered_n(double d, uint32_t precision, char* result) {
       }
     }
   }
+  if (exp_out) {
+    *exp_out = exp;
+  }
   result[index++] = 'e';
   if (exp < 0) {
     result[index++] = '-';
@@ -805,15 +735,3 @@ int d2exp_buffered_n(double d, uint32_t precision, char* result) {
 
   return index;
 }
-
-void d2exp_buffered(double d, uint32_t precision, char* result) {
-  const int len = d2exp_buffered_n(d, precision, result);
-  result[len] = '\0';
-}
-
-char* d2exp(double d, uint32_t precision) {
-  char* const buffer = (char*)malloc(2000);
-  const int index = d2exp_buffered_n(d, precision, buffer);
-  buffer[index] = '\0';
-  return buffer;
-}
diff --git a/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h b/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h
index 70857652161..1fa252959e6 100644
--- a/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h
+++ b/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h
@@ -17,7 +17,6 @@
 #ifndef RYU_D2FIXED_FULL_TABLE_H
 #define RYU_D2FIXED_FULL_TABLE_H
 
-#include <stdint.h>
 
 #define TABLE_SIZE 64
 
diff --git a/libstdc++-v3/src/c++17/ryu/d2s.c b/libstdc++-v3/src/c++17/ryu/d2s.c
index aa0da52da71..3225808c463 100644
--- a/libstdc++-v3/src/c++17/ryu/d2s.c
+++ b/libstdc++-v3/src/c++17/ryu/d2s.c
@@ -27,28 +27,15 @@
 //     size by about 10x (only one case, and only double) at the cost of some
 //     performance. Currently requires MSVC intrinsics.
 
-#include "ryu/ryu.h"
 
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
 
 #ifdef RYU_DEBUG
-#include <inttypes.h>
-#include <stdio.h>
 #endif
 
-#include "ryu/common.h"
-#include "ryu/digit_table.h"
-#include "ryu/d2s_intrinsics.h"
 
 // Include either the small or the full lookup tables depending on the mode.
 #if defined(RYU_OPTIMIZE_SIZE)
-#include "ryu/d2s_small_table.h"
 #else
-#include "ryu/d2s_full_table.h"
 #endif
 
 #define DOUBLE_MANTISSA_BITS 52
@@ -86,9 +73,10 @@ typedef struct floating_decimal_64 {
   // Decimal exponent's range is -324 to 308
   // inclusive, and can fit in a short if needed.
   int32_t exponent;
+  bool sign;
 } floating_decimal_64;
 
-static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_t ieeeExponent) {
+static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign) {
   int32_t e2;
   uint64_t m2;
   if (ieeeExponent == 0) {
@@ -308,13 +296,14 @@ static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_
   floating_decimal_64 fd;
   fd.exponent = exp;
   fd.mantissa = output;
+  fd.sign = ieeeSign;
   return fd;
 }
 
-static inline int to_chars(const floating_decimal_64 v, const bool sign, char* const result) {
+static inline int to_chars(const floating_decimal_64 v, char* const result) {
   // Step 5: Print the decimal representation.
   int index = 0;
-  if (sign) {
+  if (v.sign) {
     result[index++] = '-';
   }
 
@@ -397,29 +386,28 @@ static inline int to_chars(const floating_decimal_64 v, const bool sign, char* c
   }
 
   // Print the exponent.
-  result[index++] = 'E';
+  result[index++] = 'e';
   int32_t exp = v.exponent + (int32_t) olength - 1;
   if (exp < 0) {
     result[index++] = '-';
     exp = -exp;
-  }
+  } else
+    result[index++] = '+';
 
   if (exp >= 100) {
     const int32_t c = exp % 10;
     memcpy(result + index, DIGIT_TABLE + 2 * (exp / 10), 2);
     result[index + 2] = (char) ('0' + c);
     index += 3;
-  } else if (exp >= 10) {
+  } else {
     memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
     index += 2;
-  } else {
-    result[index++] = (char) ('0' + exp);
   }
 
   return index;
 }
 
-static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t ieeeExponent,
+static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign,
   floating_decimal_64* const v) {
   const uint64_t m2 = (1ull << DOUBLE_MANTISSA_BITS) | ieeeMantissa;
   const int32_t e2 = (int32_t) ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS;
@@ -448,10 +436,11 @@ static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t iee
   // Note: since 2^53 < 10^16, there is no need to adjust decimalLength17().
   v->mantissa = m2 >> -e2;
   v->exponent = 0;
+  v->sign = ieeeSign;
   return true;
 }
 
-int d2s_buffered_n(double f, char* result) {
+floating_decimal_64 floating_to_fd64(double f) {
   // Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
   const uint64_t bits = double_to_bits(f);
 
@@ -469,11 +458,11 @@ int d2s_buffered_n(double f, char* result) {
   const uint32_t ieeeExponent = (uint32_t) ((bits >> DOUBLE_MANTISSA_BITS) & ((1u << DOUBLE_EXPONENT_BITS) - 1));
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) {
-    return copy_special_str(result, ieeeSign, ieeeExponent, ieeeMantissa);
+    __builtin_abort();
   }
 
   floating_decimal_64 v;
-  const bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, &v);
+  const bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, ieeeSign, &v);
   if (isSmallInt) {
     // For small integers in the range [1, 2^53), v.mantissa might contain trailing (decimal) zeros.
     // For scientific notation we need to move these zeros into the exponent.
@@ -489,21 +478,8 @@ int d2s_buffered_n(double f, char* result) {
       ++v.exponent;
     }
   } else {
-    v = d2d(ieeeMantissa, ieeeExponent);
+    v = d2d(ieeeMantissa, ieeeExponent, ieeeSign);
   }
 
-  return to_chars(v, ieeeSign, result);
-}
-
-void d2s_buffered(double f, char* result) {
-  const int index = d2s_buffered_n(f, result);
-
-  // Terminate the string.
-  result[index] = '\0';
-}
-
-char* d2s(double f) {
-  char* const result = (char*) malloc(25);
-  d2s_buffered(f, result);
-  return result;
+  return v;
 }
diff --git a/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h b/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h
index ea590a60d25..fa993e6fad6 100644
--- a/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h
+++ b/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h
@@ -17,11 +17,8 @@
 #ifndef RYU_D2S_INTRINSICS_H
 #define RYU_D2S_INTRINSICS_H
 
-#include <assert.h>
-#include <stdint.h>
 
 // Defines RYU_32_BIT_PLATFORM if applicable.
-#include "ryu/common.h"
 
 // ABSL avoids uint128_t on Win32 even if __SIZEOF_INT128__ is defined.
 // Let's do the same for now.
@@ -37,7 +34,6 @@ typedef __uint128_t uint128_t;
 
 #if defined(HAS_64_BIT_INTRINSICS)
 
-#include <intrin.h>
 
 static inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) {
   return _umul128(a, b, productHi);
diff --git a/libstdc++-v3/src/c++17/ryu/f2s.c b/libstdc++-v3/src/c++17/ryu/f2s.c
index 255ecbe599c..5e635958aa1 100644
--- a/libstdc++-v3/src/c++17/ryu/f2s.c
+++ b/libstdc++-v3/src/c++17/ryu/f2s.c
@@ -18,22 +18,11 @@
 // Runtime compiler options:
 // -DRYU_DEBUG Generate verbose debugging output to stdout.
 
-#include "ryu/ryu.h"
 
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
 
 #ifdef RYU_DEBUG
-#include <stdio.h>
 #endif
 
-#include "ryu/common.h"
-#include "ryu/f2s_intrinsics.h"
-#include "ryu/digit_table.h"
 
 #define FLOAT_MANTISSA_BITS 23
 #define FLOAT_EXPONENT_BITS 8
@@ -45,9 +34,10 @@ typedef struct floating_decimal_32 {
   // Decimal exponent's range is -45 to 38
   // inclusive, and can fit in a short if needed.
   int32_t exponent;
+  bool sign;
 } floating_decimal_32;
 
-static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_t ieeeExponent) {
+static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign) {
   int32_t e2;
   uint32_t m2;
   if (ieeeExponent == 0) {
@@ -224,13 +214,14 @@ static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_
   floating_decimal_32 fd;
   fd.exponent = exp;
   fd.mantissa = output;
+  fd.sign = ieeeSign;
   return fd;
 }
 
-static inline int to_chars(const floating_decimal_32 v, const bool sign, char* const result) {
+static inline int to_chars(const floating_decimal_32 v, char* const result) {
   // Step 5: Print the decimal representation.
   int index = 0;
-  if (sign) {
+  if (v.sign) {
     result[index++] = '-';
   }
 
@@ -288,24 +279,22 @@ static inline int to_chars(const floating_decimal_32 v, const bool sign, char* c
   }
 
   // Print the exponent.
-  result[index++] = 'E';
+  result[index++] = 'e';
   int32_t exp = v.exponent + (int32_t) olength - 1;
   if (exp < 0) {
     result[index++] = '-';
     exp = -exp;
-  }
-
-  if (exp >= 10) {
-    memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
-    index += 2;
   } else {
-    result[index++] = (char) ('0' + exp);
+    result[index++] = '+';
   }
 
+  memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
+  index += 2;
+
   return index;
 }
 
-int f2s_buffered_n(float f, char* result) {
+floating_decimal_32 floating_to_fd32(float f) {
   // Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
   const uint32_t bits = float_to_bits(f);
 
@@ -324,22 +313,9 @@ int f2s_buffered_n(float f, char* result) {
 
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << FLOAT_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) {
-    return copy_special_str(result, ieeeSign, ieeeExponent, ieeeMantissa);
+    __builtin_abort();
   }
 
-  const floating_decimal_32 v = f2d(ieeeMantissa, ieeeExponent);
-  return to_chars(v, ieeeSign, result);
-}
-
-void f2s_buffered(float f, char* result) {
-  const int index = f2s_buffered_n(f, result);
-
-  // Terminate the string.
-  result[index] = '\0';
-}
-
-char* f2s(float f) {
-  char* const result = (char*) malloc(16);
-  f2s_buffered(f, result);
-  return result;
+  const floating_decimal_32 v = f2d(ieeeMantissa, ieeeExponent, ieeeSign);
+  return v;
 }
diff --git a/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h b/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h
index bb822c9a0c0..db751a41329 100644
--- a/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h
+++ b/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h
@@ -18,18 +18,14 @@
 #define RYU_F2S_INTRINSICS_H
 
 // Defines RYU_32_BIT_PLATFORM if applicable.
-#include "ryu/common.h"
 
 #if defined(RYU_FLOAT_FULL_TABLE)
 
-#include "ryu/f2s_full_table.h"
 
 #else
 
 #if defined(RYU_OPTIMIZE_SIZE)
-#include "ryu/d2s_small_table.h"
 #else
-#include "ryu/d2s_full_table.h"
 #endif
 #define FLOAT_POW5_INV_BITCOUNT (DOUBLE_POW5_INV_BITCOUNT - 64)
 #define FLOAT_POW5_BITCOUNT (DOUBLE_POW5_BITCOUNT - 64)
diff --git a/libstdc++-v3/src/c++17/ryu/generic_128.c b/libstdc++-v3/src/c++17/ryu/generic_128.c
index 2eb8da8aba8..17c1567a6a1 100644
--- a/libstdc++-v3/src/c++17/ryu/generic_128.c
+++ b/libstdc++-v3/src/c++17/ryu/generic_128.c
@@ -37,42 +37,9 @@ static char* s(uint128_t v) {
 
 #define ONE ((uint128_t) 1)
 
-#define FLOAT_MANTISSA_BITS 23
-#define FLOAT_EXPONENT_BITS 8
-
-struct floating_decimal_128 float_to_fd128(float f) {
-  uint32_t bits = 0;
-  memcpy(&bits, &f, sizeof(float));
-  return generic_binary_to_decimal(bits, FLOAT_MANTISSA_BITS, FLOAT_EXPONENT_BITS, false);
-}
-
-#define DOUBLE_MANTISSA_BITS 52
-#define DOUBLE_EXPONENT_BITS 11
-
-struct floating_decimal_128 double_to_fd128(double d) {
-  uint64_t bits = 0;
-  memcpy(&bits, &d, sizeof(double));
-  return generic_binary_to_decimal(bits, DOUBLE_MANTISSA_BITS, DOUBLE_EXPONENT_BITS, false);
-}
-
-#define LONG_DOUBLE_MANTISSA_BITS 64
-#define LONG_DOUBLE_EXPONENT_BITS 15
-
-struct floating_decimal_128 long_double_to_fd128(long double d) {
-  uint128_t bits = 0;
-  memcpy(&bits, &d, sizeof(long double));
-#ifdef RYU_DEBUG
-  // For some odd reason, this ends up with noise in the top 48 bits. We can
-  // clear out those bits with the following line; this is not required, the
-  // conversion routine should ignore those bits, but the debug output can be
-  // confusing if they aren't 0s.
-  bits &= (ONE << 80) - 1;
-#endif
-  return generic_binary_to_decimal(bits, LONG_DOUBLE_MANTISSA_BITS, LONG_DOUBLE_EXPONENT_BITS, true);
-}
-
 struct floating_decimal_128 generic_binary_to_decimal(
-    const uint128_t bits, const uint32_t mantissaBits, const uint32_t exponentBits, const bool explicitLeadingBit) {
+    const uint128_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign,
+    const uint32_t mantissaBits, const uint32_t exponentBits, const bool explicitLeadingBit) {
 #ifdef RYU_DEBUG
   printf("IN=");
   for (int32_t bit = 127; bit >= 0; --bit) {
@@ -82,9 +49,6 @@ struct floating_decimal_128 generic_binary_to_decimal(
 #endif
 
   const uint32_t bias = (1u << (exponentBits - 1)) - 1;
-  const bool ieeeSign = ((bits >> (mantissaBits + exponentBits)) & 1) != 0;
-  const uint128_t ieeeMantissa = bits & ((ONE << mantissaBits) - 1);
-  const uint32_t ieeeExponent = (uint32_t) ((bits >> mantissaBits) & ((ONE << exponentBits) - 1u));
 
   if (ieeeExponent == 0 && ieeeMantissa == 0) {
     struct floating_decimal_128 fd;
@@ -320,14 +284,17 @@ int generic_to_chars(const struct floating_decimal_128 v, char* const result) {
   }
 
   // Print the exponent.
-  result[index++] = 'E';
+  result[index++] = 'e';
   int32_t exp = v.exponent + olength - 1;
   if (exp < 0) {
     result[index++] = '-';
     exp = -exp;
-  }
+  } else
+    result[index++] = '+';
 
   uint32_t elength = decimalLength(exp);
+  if (elength == 1)
+    ++elength;
   for (uint32_t i = 0; i < elength; ++i) {
     const uint32_t c = exp % 10;
     exp /= 10;
Jonathan Wakely Dec. 17, 2020, 2:35 p.m. UTC | #2
On 15/07/20 14:12 -0400, Patrick Palka via Libstdc++ wrote:
>On Tue, 14 Jul 2020, Patrick Palka wrote:
>
>> This performs the following modifications to our local copy of Ryu in
>> order to make it more easily usable for our std::to_chars implementation:
>>
>>   * Remove all #includes
>>   * Remove copy_special_str routines
>>   * Adjust the exponent formatting to match printf
>>   * Remove some functions we're not going to use
>>   * Add an out-parameter to d2exp_buffered_n for the scientific exponent
>>   * Store the sign bit inside struct floating_decimal_[32|64]
>>   * Rename [df]2s_buffered_n and change their return type
>
>Here is v2 of this patch, which applies another modification to Ryu that
>v2 of the third patch in the series depends on:

OK, thanks.
diff mbox series

Patch

diff --git a/libstdc++-v3/src/c++17/ryu/common.h b/libstdc++-v3/src/c++17/ryu/common.h
index 7dc130947ac..f8ee147db04 100644
--- a/libstdc++-v3/src/c++17/ryu/common.h
+++ b/libstdc++-v3/src/c++17/ryu/common.h
@@ -17,9 +17,6 @@ 
 #ifndef RYU_COMMON_H
 #define RYU_COMMON_H
 
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
 
 #if defined(_M_IX86) || defined(_M_ARM)
 #define RYU_32_BIT_PLATFORM
@@ -83,22 +80,6 @@  static inline uint32_t log10Pow5(const int32_t e) {
   return (((uint32_t) e) * 732923) >> 20;
 }
 
-static inline int copy_special_str(char * const result, const bool sign, const bool exponent, const bool mantissa) {
-  if (mantissa) {
-    memcpy(result, "NaN", 3);
-    return 3;
-  }
-  if (sign) {
-    result[0] = '-';
-  }
-  if (exponent) {
-    memcpy(result + sign, "Infinity", 8);
-    return sign + 8;
-  }
-  memcpy(result + sign, "0E0", 3);
-  return sign + 3;
-}
-
 static inline uint32_t float_to_bits(const float f) {
   uint32_t bits = 0;
   memcpy(&bits, &f, sizeof(float));
diff --git a/libstdc++-v3/src/c++17/ryu/d2fixed.c b/libstdc++-v3/src/c++17/ryu/d2fixed.c
index 5f479abb91b..642a29d3010 100644
--- a/libstdc++-v3/src/c++17/ryu/d2fixed.c
+++ b/libstdc++-v3/src/c++17/ryu/d2fixed.c
@@ -23,23 +23,11 @@ 
 //
 // -DRYU_AVOID_UINT128 Avoid using uint128_t. Slower, depending on your compiler.
 
-#include "ryu/ryu.h"
 
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
 
 #ifdef RYU_DEBUG
-#include <inttypes.h>
-#include <stdio.h>
 #endif
 
-#include "ryu/common.h"
-#include "ryu/digit_table.h"
-#include "ryu/d2fixed_full_table.h"
-#include "ryu/d2s_intrinsics.h"
 
 #define DOUBLE_MANTISSA_BITS 52
 #define DOUBLE_EXPONENT_BITS 11
@@ -328,33 +316,6 @@  static inline uint32_t lengthForIndex(const uint32_t idx) {
   return (log10Pow2(16 * (int32_t) idx) + 1 + 16 + 8) / 9;
 }
 
-static inline int copy_special_str_printf(char* const result, const bool sign, const uint64_t mantissa) {
-#if defined(_MSC_VER)
-  // TODO: Check that -nan is expected output on Windows.
-  if (sign) {
-    result[0] = '-';
-  }
-  if (mantissa) {
-    if (mantissa < (1ull << (DOUBLE_MANTISSA_BITS - 1))) {
-      memcpy(result + sign, "nan(snan)", 9);
-      return sign + 9;
-    }
-    memcpy(result + sign, "nan", 3);
-    return sign + 3;
-  }
-#else
-  if (mantissa) {
-    memcpy(result, "nan", 3);
-    return 3;
-  }
-  if (sign) {
-    result[0] = '-';
-  }
-#endif
-  memcpy(result + sign, "Infinity", 8);
-  return sign + 8;
-}
-
 int d2fixed_buffered_n(double d, uint32_t precision, char* result) {
   const uint64_t bits = double_to_bits(d);
 #ifdef RYU_DEBUG
@@ -372,20 +333,10 @@  int d2fixed_buffered_n(double d, uint32_t precision, char* result) {
 
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u)) {
-    return copy_special_str_printf(result, ieeeSign, ieeeMantissa);
+    __builtin_abort();
   }
   if (ieeeExponent == 0 && ieeeMantissa == 0) {
-    int index = 0;
-    if (ieeeSign) {
-      result[index++] = '-';
-    }
-    result[index++] = '0';
-    if (precision > 0) {
-      result[index++] = '.';
-      memset(result + index, '0', precision);
-      index += precision;
-    }
-    return index;
+    __builtin_abort();
   }
 
   int32_t e2;
@@ -549,21 +500,9 @@  int d2fixed_buffered_n(double d, uint32_t precision, char* result) {
   return index;
 }
 
-void d2fixed_buffered(double d, uint32_t precision, char* result) {
-  const int len = d2fixed_buffered_n(d, precision, result);
-  result[len] = '\0';
-}
-
-char* d2fixed(double d, uint32_t precision) {
-  char* const buffer = (char*)malloc(2000);
-  const int index = d2fixed_buffered_n(d, precision, buffer);
-  buffer[index] = '\0';
-  return buffer;
-}
-
 
 
-int d2exp_buffered_n(double d, uint32_t precision, char* result) {
+int d2exp_buffered_n(double d, uint32_t precision, char* result, int* exp_out) {
   const uint64_t bits = double_to_bits(d);
 #ifdef RYU_DEBUG
   printf("IN=");
@@ -580,22 +519,10 @@  int d2exp_buffered_n(double d, uint32_t precision, char* result) {
 
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u)) {
-    return copy_special_str_printf(result, ieeeSign, ieeeMantissa);
+    __builtin_abort();
   }
   if (ieeeExponent == 0 && ieeeMantissa == 0) {
-    int index = 0;
-    if (ieeeSign) {
-      result[index++] = '-';
-    }
-    result[index++] = '0';
-    if (precision > 0) {
-      result[index++] = '.';
-      memset(result + index, '0', precision);
-      index += precision;
-    }
-    memcpy(result + index, "e+00", 4);
-    index += 4;
-    return index;
+    __builtin_abort();
   }
 
   int32_t e2;
@@ -785,6 +712,9 @@  int d2exp_buffered_n(double d, uint32_t precision, char* result) {
       }
     }
   }
+  if (exp_out) {
+    *exp_out = exp;
+  }
   result[index++] = 'e';
   if (exp < 0) {
     result[index++] = '-';
@@ -805,15 +735,3 @@  int d2exp_buffered_n(double d, uint32_t precision, char* result) {
 
   return index;
 }
-
-void d2exp_buffered(double d, uint32_t precision, char* result) {
-  const int len = d2exp_buffered_n(d, precision, result);
-  result[len] = '\0';
-}
-
-char* d2exp(double d, uint32_t precision) {
-  char* const buffer = (char*)malloc(2000);
-  const int index = d2exp_buffered_n(d, precision, buffer);
-  buffer[index] = '\0';
-  return buffer;
-}
diff --git a/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h b/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h
index 70857652161..1fa252959e6 100644
--- a/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h
+++ b/libstdc++-v3/src/c++17/ryu/d2fixed_full_table.h
@@ -17,7 +17,6 @@ 
 #ifndef RYU_D2FIXED_FULL_TABLE_H
 #define RYU_D2FIXED_FULL_TABLE_H
 
-#include <stdint.h>
 
 #define TABLE_SIZE 64
 
diff --git a/libstdc++-v3/src/c++17/ryu/d2s.c b/libstdc++-v3/src/c++17/ryu/d2s.c
index aa0da52da71..3225808c463 100644
--- a/libstdc++-v3/src/c++17/ryu/d2s.c
+++ b/libstdc++-v3/src/c++17/ryu/d2s.c
@@ -27,28 +27,15 @@ 
 //     size by about 10x (only one case, and only double) at the cost of some
 //     performance. Currently requires MSVC intrinsics.
 
-#include "ryu/ryu.h"
 
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
 
 #ifdef RYU_DEBUG
-#include <inttypes.h>
-#include <stdio.h>
 #endif
 
-#include "ryu/common.h"
-#include "ryu/digit_table.h"
-#include "ryu/d2s_intrinsics.h"
 
 // Include either the small or the full lookup tables depending on the mode.
 #if defined(RYU_OPTIMIZE_SIZE)
-#include "ryu/d2s_small_table.h"
 #else
-#include "ryu/d2s_full_table.h"
 #endif
 
 #define DOUBLE_MANTISSA_BITS 52
@@ -86,9 +73,10 @@  typedef struct floating_decimal_64 {
   // Decimal exponent's range is -324 to 308
   // inclusive, and can fit in a short if needed.
   int32_t exponent;
+  bool sign;
 } floating_decimal_64;
 
-static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_t ieeeExponent) {
+static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign) {
   int32_t e2;
   uint64_t m2;
   if (ieeeExponent == 0) {
@@ -308,13 +296,14 @@  static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_
   floating_decimal_64 fd;
   fd.exponent = exp;
   fd.mantissa = output;
+  fd.sign = ieeeSign;
   return fd;
 }
 
-static inline int to_chars(const floating_decimal_64 v, const bool sign, char* const result) {
+static inline int to_chars(const floating_decimal_64 v, char* const result) {
   // Step 5: Print the decimal representation.
   int index = 0;
-  if (sign) {
+  if (v.sign) {
     result[index++] = '-';
   }
 
@@ -397,29 +386,28 @@  static inline int to_chars(const floating_decimal_64 v, const bool sign, char* c
   }
 
   // Print the exponent.
-  result[index++] = 'E';
+  result[index++] = 'e';
   int32_t exp = v.exponent + (int32_t) olength - 1;
   if (exp < 0) {
     result[index++] = '-';
     exp = -exp;
-  }
+  } else
+    result[index++] = '+';
 
   if (exp >= 100) {
     const int32_t c = exp % 10;
     memcpy(result + index, DIGIT_TABLE + 2 * (exp / 10), 2);
     result[index + 2] = (char) ('0' + c);
     index += 3;
-  } else if (exp >= 10) {
+  } else {
     memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
     index += 2;
-  } else {
-    result[index++] = (char) ('0' + exp);
   }
 
   return index;
 }
 
-static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t ieeeExponent,
+static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign,
   floating_decimal_64* const v) {
   const uint64_t m2 = (1ull << DOUBLE_MANTISSA_BITS) | ieeeMantissa;
   const int32_t e2 = (int32_t) ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS;
@@ -448,10 +436,11 @@  static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t iee
   // Note: since 2^53 < 10^16, there is no need to adjust decimalLength17().
   v->mantissa = m2 >> -e2;
   v->exponent = 0;
+  v->sign = ieeeSign;
   return true;
 }
 
-int d2s_buffered_n(double f, char* result) {
+floating_decimal_64 floating_to_fd64(double f) {
   // Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
   const uint64_t bits = double_to_bits(f);
 
@@ -469,11 +458,11 @@  int d2s_buffered_n(double f, char* result) {
   const uint32_t ieeeExponent = (uint32_t) ((bits >> DOUBLE_MANTISSA_BITS) & ((1u << DOUBLE_EXPONENT_BITS) - 1));
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) {
-    return copy_special_str(result, ieeeSign, ieeeExponent, ieeeMantissa);
+    __builtin_abort();
   }
 
   floating_decimal_64 v;
-  const bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, &v);
+  const bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, ieeeSign, &v);
   if (isSmallInt) {
     // For small integers in the range [1, 2^53), v.mantissa might contain trailing (decimal) zeros.
     // For scientific notation we need to move these zeros into the exponent.
@@ -489,21 +478,8 @@  int d2s_buffered_n(double f, char* result) {
       ++v.exponent;
     }
   } else {
-    v = d2d(ieeeMantissa, ieeeExponent);
+    v = d2d(ieeeMantissa, ieeeExponent, ieeeSign);
   }
 
-  return to_chars(v, ieeeSign, result);
-}
-
-void d2s_buffered(double f, char* result) {
-  const int index = d2s_buffered_n(f, result);
-
-  // Terminate the string.
-  result[index] = '\0';
-}
-
-char* d2s(double f) {
-  char* const result = (char*) malloc(25);
-  d2s_buffered(f, result);
-  return result;
+  return v;
 }
diff --git a/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h b/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h
index ea590a60d25..fa993e6fad6 100644
--- a/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h
+++ b/libstdc++-v3/src/c++17/ryu/d2s_intrinsics.h
@@ -17,11 +17,8 @@ 
 #ifndef RYU_D2S_INTRINSICS_H
 #define RYU_D2S_INTRINSICS_H
 
-#include <assert.h>
-#include <stdint.h>
 
 // Defines RYU_32_BIT_PLATFORM if applicable.
-#include "ryu/common.h"
 
 // ABSL avoids uint128_t on Win32 even if __SIZEOF_INT128__ is defined.
 // Let's do the same for now.
@@ -37,7 +34,6 @@  typedef __uint128_t uint128_t;
 
 #if defined(HAS_64_BIT_INTRINSICS)
 
-#include <intrin.h>
 
 static inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) {
   return _umul128(a, b, productHi);
diff --git a/libstdc++-v3/src/c++17/ryu/f2s.c b/libstdc++-v3/src/c++17/ryu/f2s.c
index 255ecbe599c..5e635958aa1 100644
--- a/libstdc++-v3/src/c++17/ryu/f2s.c
+++ b/libstdc++-v3/src/c++17/ryu/f2s.c
@@ -18,22 +18,11 @@ 
 // Runtime compiler options:
 // -DRYU_DEBUG Generate verbose debugging output to stdout.
 
-#include "ryu/ryu.h"
 
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
 
 #ifdef RYU_DEBUG
-#include <stdio.h>
 #endif
 
-#include "ryu/common.h"
-#include "ryu/f2s_intrinsics.h"
-#include "ryu/digit_table.h"
 
 #define FLOAT_MANTISSA_BITS 23
 #define FLOAT_EXPONENT_BITS 8
@@ -45,9 +34,10 @@  typedef struct floating_decimal_32 {
   // Decimal exponent's range is -45 to 38
   // inclusive, and can fit in a short if needed.
   int32_t exponent;
+  bool sign;
 } floating_decimal_32;
 
-static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_t ieeeExponent) {
+static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_t ieeeExponent, const bool ieeeSign) {
   int32_t e2;
   uint32_t m2;
   if (ieeeExponent == 0) {
@@ -224,13 +214,14 @@  static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_
   floating_decimal_32 fd;
   fd.exponent = exp;
   fd.mantissa = output;
+  fd.sign = ieeeSign;
   return fd;
 }
 
-static inline int to_chars(const floating_decimal_32 v, const bool sign, char* const result) {
+static inline int to_chars(const floating_decimal_32 v, char* const result) {
   // Step 5: Print the decimal representation.
   int index = 0;
-  if (sign) {
+  if (v.sign) {
     result[index++] = '-';
   }
 
@@ -288,24 +279,22 @@  static inline int to_chars(const floating_decimal_32 v, const bool sign, char* c
   }
 
   // Print the exponent.
-  result[index++] = 'E';
+  result[index++] = 'e';
   int32_t exp = v.exponent + (int32_t) olength - 1;
   if (exp < 0) {
     result[index++] = '-';
     exp = -exp;
-  }
-
-  if (exp >= 10) {
-    memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
-    index += 2;
   } else {
-    result[index++] = (char) ('0' + exp);
+    result[index++] = '+';
   }
 
+  memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
+  index += 2;
+
   return index;
 }
 
-int f2s_buffered_n(float f, char* result) {
+floating_decimal_32 floating_to_fd32(float f) {
   // Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
   const uint32_t bits = float_to_bits(f);
 
@@ -324,22 +313,9 @@  int f2s_buffered_n(float f, char* result) {
 
   // Case distinction; exit early for the easy cases.
   if (ieeeExponent == ((1u << FLOAT_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) {
-    return copy_special_str(result, ieeeSign, ieeeExponent, ieeeMantissa);
+    __builtin_abort();
   }
 
-  const floating_decimal_32 v = f2d(ieeeMantissa, ieeeExponent);
-  return to_chars(v, ieeeSign, result);
-}
-
-void f2s_buffered(float f, char* result) {
-  const int index = f2s_buffered_n(f, result);
-
-  // Terminate the string.
-  result[index] = '\0';
-}
-
-char* f2s(float f) {
-  char* const result = (char*) malloc(16);
-  f2s_buffered(f, result);
-  return result;
+  const floating_decimal_32 v = f2d(ieeeMantissa, ieeeExponent, ieeeSign);
+  return v;
 }
diff --git a/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h b/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h
index bb822c9a0c0..db751a41329 100644
--- a/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h
+++ b/libstdc++-v3/src/c++17/ryu/f2s_intrinsics.h
@@ -18,18 +18,14 @@ 
 #define RYU_F2S_INTRINSICS_H
 
 // Defines RYU_32_BIT_PLATFORM if applicable.
-#include "ryu/common.h"
 
 #if defined(RYU_FLOAT_FULL_TABLE)
 
-#include "ryu/f2s_full_table.h"
 
 #else
 
 #if defined(RYU_OPTIMIZE_SIZE)
-#include "ryu/d2s_small_table.h"
 #else
-#include "ryu/d2s_full_table.h"
 #endif
 #define FLOAT_POW5_INV_BITCOUNT (DOUBLE_POW5_INV_BITCOUNT - 64)
 #define FLOAT_POW5_BITCOUNT (DOUBLE_POW5_BITCOUNT - 64)
diff --git a/libstdc++-v3/src/c++17/ryu/generic_128.c b/libstdc++-v3/src/c++17/ryu/generic_128.c
index 2eb8da8aba8..fca6ecb8412 100644
--- a/libstdc++-v3/src/c++17/ryu/generic_128.c
+++ b/libstdc++-v3/src/c++17/ryu/generic_128.c
@@ -320,14 +320,17 @@  int generic_to_chars(const struct floating_decimal_128 v, char* const result) {
   }
 
   // Print the exponent.
-  result[index++] = 'E';
+  result[index++] = 'e';
   int32_t exp = v.exponent + olength - 1;
   if (exp < 0) {
     result[index++] = '-';
     exp = -exp;
-  }
+  } else
+    result[index++] = '+';
 
   uint32_t elength = decimalLength(exp);
+  if (elength == 1)
+    ++elength;
   for (uint32_t i = 0; i < elength; ++i) {
     const uint32_t c = exp % 10;
     exp /= 10;