json BUGFIX in ::lyjson_number()

The ::lyjson_number() has been rewritten because of frequent errors.
A new ::lyjson_exp_number() feature has been added and tests have also
been rewritten.
diff --git a/src/json.c b/src/json.c
index 77fdf74..6ae4484 100644
--- a/src/json.c
+++ b/src/json.c
@@ -322,6 +322,61 @@
 }
 
 /**
+ * @brief Calculate how many @p c characters there are in a row.
+ *
+ * @param[in] str Count from this position.
+ * @param[in] end Position after the last checked character.
+ * @param[in] c Checked character.
+ * @param[in] backwards Set to 1, if to proceed from end-1 to str.
+ * @return Number of characters in a row.
+ */
+static uint32_t
+lyjson_count_in_row(const char *str, const char *end, char c, ly_bool backwards)
+{
+    uint32_t cnt;
+
+    assert(str && end);
+
+    if (str >= end) {
+        return 0;
+    }
+
+    if (!backwards) {
+        for (cnt = 0; (str != end) && (*str == c); ++str, ++cnt) {}
+    } else {
+        --end;
+        --str;
+        for (cnt = 0; (str != end) && (*end == c); --end, ++cnt) {}
+    }
+
+    return cnt;
+}
+
+/**
+ * @brief Check if the number can be shortened to zero.
+ *
+ * The input number must be syntactically valid.
+ *
+ * @param[in] in Start of input string;
+ * @param[in] end End of input string;
+ * @return 1 if number is zero, otherwise 0.
+ */
+static ly_bool
+lyjson_number_is_zero(const char *in, const char *end)
+{
+    assert(end >= in);
+
+    if ((in[0] == '-') || (in[0] == '+')) {
+        in++;
+    }
+    if ((in[0] == '0') && (in[1] == '.')) {
+        in += 2;
+    }
+
+    return lyjson_count_in_row(in, end, '0', 0) == end - in;
+}
+
+/**
  * @brief Allocate buffer for number in string format.
  *
  * @param[in] jsonctx JSON context.
@@ -331,25 +386,264 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyjson_get_buffer_for_number(struct lyjson_ctx *jsonctx, size_t num_len, char **buffer)
+lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint32_t num_len, char **buffer)
 {
     *buffer = NULL;
 
-    LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
+    LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(ctx, LYVE_SEMANTICS,
             "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
 
-    /* allocate buffer for the result (add terminating NULL-byte) */
+    /* allocate buffer for the result (add NULL-byte) */
     *buffer = malloc(num_len + 1);
-    LY_CHECK_ERR_RET(!(*buffer), LOGMEM(jsonctx->ctx), LY_EMEM);
+    LY_CHECK_ERR_RET(!(*buffer), LOGMEM(ctx), LY_EMEM);
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Copy the 'numeric part' (@p num) except its decimal point
+ * (@p dec_point) and insert the new decimal point (@p dp_position)
+ * only if it is to be placed in the 'numeric part' range (@p num).
+ *
+ * @param[in] num Begin of the 'numeric part'.
+ * @param[in] num_len Length of the 'numeric part'.
+ * @param[in] dec_point Pointer to the old decimal point.
+ * If it has a NULL value, it is ignored.
+ * @param[in] dp_position Position of the new decimal point.
+ * If it has a negative value, it is ignored.
+ * @param[out] dst Memory into which the copied result is written.
+ * @return Number of characters written to the @p dst.
+ */
+static uint32_t
+lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
+        char *dec_point, int32_t dp_position, char *dst)
+{
+    int32_t dec_point_idx;
+    int32_t n, d;
+
+    assert(num && dst);
+
+    dec_point_idx = dec_point ? dec_point - num : INT32_MAX;
+    assert((dec_point_idx >= 0) && (dec_point_idx != dp_position));
+
+    for (n = 0, d = 0; (uint32_t)n < num_len; n++) {
+        if (n == dec_point_idx) {
+            continue;
+        } else if (d == dp_position) {
+            dst[d++] = '.';
+            dst[d++] = num[n];
+        } else {
+            dst[d++] = num[n];
+        }
+    }
+
+    return d;
+}
+
+/**
+ * @brief Convert JSON number with exponent into the representation
+ * used by YANG.
+ *
+ * The input numeric string must be syntactically valid. Also, before
+ * calling this function, checks should be performed using the
+ * ::lyjson_number_is_zero().
+ *
+ * @param[in] ctx Context for the error message.
+ * @param[in] in Beginning of the string containing the number.
+ * @param[in] exponent Pointer to the letter E/e.
+ * @param[in] total_len Total size of the input number.
+ * @param[out] res Conversion result.
+ * @param[out] res_len Length of the result.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent,
+        size_t total_len, char **res, size_t *res_len)
+{
+
+#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
+        if (FLAG) { \
+            ARRAY[INDEX++] = '-'; \
+        }
+
+/* Length of leading zero followed by the decimal point. */
+#define LEADING_ZERO 1
+
+/* Flags for the ::lyjson_count_in_row() */
+#define FORWARD 0
+#define BACKWARD 1
+
+    /* Buffer where the result is stored. */
+    char *buf;
+    /* Size without space for terminating NULL-byte. */
+    uint32_t buf_len;
+    /* Index to buf. */
+    uint32_t i = 0;
+    /* A 'numeric part' doesn't contain a minus sign or an leading zero.
+     * For example, in 0.45, there is the leading zero.
+     */
+    const char *num;
+    /* Length of the 'numeric part' ends before E/e. */
+    uint32_t num_len;
+    /* Position of decimal point in the num. */
+    char *dec_point;
+    /* Final position of decimal point in the buf. */
+    int32_t dp_position;
+    /* Exponent as integer. */
+    long int e_val;
+    /* Byte for the decimal point. */
+    int8_t dot;
+    /* Required additional byte for the minus sign. */
+    uint8_t minus;
+    /* The number of zeros. */
+    long zeros;
+    /* If the number starts with leading zero followed by the decimal point. */
+    ly_bool leading_zero;
+
+    assert(ctx && in && exponent && res && res_len && (total_len > 2));
+    assert((in < exponent) && ((*exponent == 'e') || (*exponent == 'E')));
+
+    /* Convert exponent. */
+    errno = 0;
+    e_val = strtol(exponent + 1, NULL, LY_BASE_DEC);
+    if (errno) {
+        LOGVAL(ctx, LYVE_SEMANTICS,
+                "Exponent out-of-bounds in a JSON Number value (%.*s).",
+                total_len, in);
+        return LY_EVALID;
+    }
+
+    minus = in[0] == '-';
+    if (in[minus] == '0') {
+        assert(in[minus + 1] == '.');
+        leading_zero = 1;
+        /* The leading zero has been found, it will be skipped. */
+        num = &in[minus + 1];
+    } else {
+        leading_zero = 0;
+        /* Set to the first number. */
+        num = &in[minus];
+    }
+    num_len = exponent - num;
+
+    /* Find the location of the decimal points. */
+    dec_point = ly_strnchr(num, '.', num_len);
+    dp_position = dec_point ?
+            dec_point - num + e_val :
+            num_len + e_val;
+
+    /* Remove zeros after the decimal point from the end of
+     * the 'numeric part' because these are useless.
+     * (For example, in 40.001000 these are the last 3).
+     */
+    num_len -= dp_position > 0 ?
+            lyjson_count_in_row(num + dp_position - 1, exponent, '0', BACKWARD) :
+            lyjson_count_in_row(num, exponent, '0', BACKWARD);
+
+    /* Decide what to do with the dot from the 'numeric part'. */
+    if (dec_point && ((int32_t)(num_len - 1) == dp_position)) {
+        /* Decimal point in the last place is useless. */
+        dot = -1;
+    } else if (dec_point) {
+        /* Decimal point is shifted. */
+        dot = 0;
+    } else {
+        /* Additional byte for the decimal point is requred. */
+        dot = 1;
+    }
+
+    /* Final composition of the result. */
+    if (dp_position <= 0) {
+        /* Adding decimal point before the integer with adding additional zero(s). */
+
+        zeros = labs(dp_position);
+        buf_len = minus + LEADING_ZERO + dot + zeros + num_len;
+        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+        MAYBE_WRITE_MINUS(buf, i, minus);
+        buf[i++] = '0';
+        buf[i++] = '.';
+        memset(buf + i, '0', zeros);
+        i += zeros;
+        dp_position = -1;
+        lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
+    } else if (leading_zero && (dp_position < (ssize_t)num_len)) {
+        /* Insert decimal point between the integer's digits. */
+
+        /* Set a new range of 'numeric part'. Old decimal point is skipped. */
+        num++;
+        num_len--;
+        dp_position--;
+        /* Get the number of useless zeros between the old
+         * and new decimal point. For example, in the number 0.005E1,
+         * there is one useless zero.
+         */
+        zeros = lyjson_count_in_row(num, num + dp_position + 1, '0', FORWARD);
+        /* If the new decimal point will be in the place of the first non-zero subnumber. */
+        if (zeros == (dp_position + 1)) {
+            /* keep one zero as leading zero */
+            zeros--;
+            /* new decimal point will be behind the leading zero */
+            dp_position = 1;
+            dot = 1;
+        } else {
+            dot = 0;
+        }
+        buf_len = minus + dot + (num_len - zeros);
+        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+        MAYBE_WRITE_MINUS(buf, i, minus);
+        /* Skip useless zeros and copy. */
+        lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
+    } else if (dp_position < (ssize_t)num_len) {
+        /* Insert decimal point between the integer's digits. */
+
+        buf_len = minus + dot + num_len;
+        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+        MAYBE_WRITE_MINUS(buf, i, minus);
+        lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
+    } else if (leading_zero) {
+        /* Adding decimal point after the decimal value make the integer result. */
+
+        /* Set a new range of 'numeric part'. Old decimal point is skipped. */
+        num++;
+        num_len--;
+        /* Get the number of useless zeros. */
+        zeros = lyjson_count_in_row(num, num + num_len, '0', FORWARD);
+        buf_len = minus + dp_position - zeros;
+        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+        MAYBE_WRITE_MINUS(buf, i, minus);
+        /* Skip useless zeros and copy. */
+        i += lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
+        /* Add multiples of ten behind the 'numeric part'. */
+        memset(buf + i, '0', buf_len - i);
+    } else {
+        /* Adding decimal point after the decimal value make the integer result. */
+
+        buf_len = minus + dp_position;
+        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
+        MAYBE_WRITE_MINUS(buf, i, minus);
+        i += lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
+        /* Add multiples of ten behind the 'numeric part'. */
+        memset(buf + i, '0', buf_len - i);
+    }
+
+    buf[buf_len] = '\0';
+    *res = buf;
+    *res_len = buf_len;
+
+#undef MAYBE_WRITE_MINUS
+#undef LEADING_ZERO
+#undef FORWARD
+#undef BACKWARD
+
     return LY_SUCCESS;
 }
 
 static LY_ERR
 lyjson_number(struct lyjson_ctx *jsonctx)
 {
-    size_t offset = 0, exponent = 0;
-    const char *in = jsonctx->in->current;
+    size_t offset = 0, num_len;
+    const char *in = jsonctx->in->current, *exponent = NULL;
     uint8_t minus = 0;
+    char *num;
 
     if (in[offset] == '-') {
         ++offset;
@@ -384,7 +678,8 @@
     }
 
     if ((in[offset] == 'e') || (in[offset] == 'E')) {
-        exponent = offset++;
+        exponent = &in[offset];
+        ++offset;
         if ((in[offset] == '+') || (in[offset] == '-')) {
             ++offset;
         }
@@ -396,117 +691,16 @@
         }
     }
 
-    if (exponent) {
-        /* convert JSON number with exponent into the representation used by YANG */
-        long int  e_val;
-        char *ptr, *dec_point, *num;
-        const char *e_ptr = &in[exponent + 1];
-        size_t num_len, i;
-        int64_t dp_position; /* final position of the deciaml point */
-
-        errno = 0;
-        e_val = strtol(e_ptr, &ptr, LY_BASE_DEC);
-        if (errno) {
-            LOGVAL(jsonctx->ctx, LYVE_SEMANTICS, "Exponent out-of-bounds in a JSON Number value (%.*s).",
-                    (int)(offset - minus - (e_ptr - in)), e_ptr);
-            return LY_EVALID;
-        }
-
-        if (!e_val) {
-            /* exponent is zero, so just cut the part with the exponent */
-            num_len = exponent;
-            LY_CHECK_RET(lyjson_get_buffer_for_number(jsonctx, num_len, &num));
-            memcpy(num, in, num_len);
-            num[num_len] = '\0';
-            goto store_exp_number;
-        }
-
-        dec_point = ly_strnchr(in, '.', exponent);
-        if (!dec_point) {
-            /* value is integer, we are just ... */
-            if (e_val >= 0) {
-                /* adding zeros at the end */
-                num_len = exponent + e_val;
-                dp_position = num_len; /* decimal point is behind the actual value */
-            } else if ((size_t)labs(e_val) < (exponent - minus)) {
-                /* adding decimal point between the integer's digits */
-                num_len = exponent + 1;
-                dp_position = exponent + e_val;
-            } else {
-                /* adding decimal point before the integer with adding leading zero(s) */
-                num_len = labs(e_val) + 2 + minus;
-                dp_position = exponent + e_val;
-            }
-            dp_position -= minus;
-        } else {
-            /* value is decimal, we are moving the decimal point */
-            dp_position = dec_point - in + e_val - minus;
-            if (dp_position > (ssize_t)exponent) {
-                /* moving decimal point after the decimal value make the integer result */
-                num_len = dp_position;
-            } else if (dp_position < 0) {
-                /* moving decimal point before the decimal value requires additional zero(s)
-                 * (decimal point is already count in exponent value) */
-                num_len = exponent + labs(dp_position) + 1;
-            } else if (dp_position == 0) {
-                /* moving the decimal point exactly to the beginning will cause a zero character to be added. */
-                num_len = exponent + 1;
-            } else {
-                /* moving decimal point just inside the decimal value does not make any change in length */
-                num_len = exponent;
-            }
-        }
-
-        LY_CHECK_RET(lyjson_get_buffer_for_number(jsonctx, num_len, &num));
-
-        /* compose the resulting vlaue */
-        i = 0;
-        if (minus) {
-            num[i++] = '-';
-        }
-        /* add leading zeros */
-        if (dp_position <= 0) {
-            num[i++] = '0';
-            num[i++] = '.';
-            for ( ; dp_position; dp_position++) {
-                num[i++] = '0';
-            }
-        }
-        /* copy the value */
-        ly_bool dp_placed;
-        size_t j;
-        for (dp_placed = dp_position ? 0 : 1, j = minus; j < exponent; j++) {
-            if (in[j] == '.') {
-                continue;
-            }
-            if (!dp_placed) {
-                if (!dp_position) {
-                    num[i++] = '.';
-                    dp_placed = 1;
-                } else {
-                    dp_position--;
-                    if (in[j] == '0') {
-                        num_len--;
-                        continue;
-                    }
-                }
-            }
-
-            num[i++] = in[j];
-        }
-        /* trailing zeros */
-        while (dp_position--) {
-            num[i++] = '0';
-        }
-        /* terminating NULL byte */
-        num[i] = '\0';
-
-store_exp_number:
-        /* store the modified number */
+    if (lyjson_number_is_zero(in, exponent ? exponent : &in[offset])) {
+        lyjson_ctx_set_value(jsonctx, in, minus + 1, 0);
+    } else if (exponent && lyjson_number_is_zero(exponent + 1, &in[offset])) {
+        lyjson_ctx_set_value(jsonctx, in, exponent - in, 0);
+    } else if (exponent) {
+        LY_CHECK_RET(lyjson_exp_number(jsonctx->ctx, in, exponent, offset, &num, &num_len));
         lyjson_ctx_set_value(jsonctx, num, num_len, 1);
     } else {
         /* store the number */
-        lyjson_ctx_set_value(jsonctx, jsonctx->in->current, offset, 0);
+        lyjson_ctx_set_value(jsonctx, in, offset, 0);
     }
     ly_in_skip(jsonctx->in, offset);
 
diff --git a/tests/utests/basic/test_json.c b/tests/utests/basic/test_json.c
index 211e506..cabafaa 100644
--- a/tests/utests/basic/test_json.c
+++ b/tests/utests/basic/test_json.c
@@ -104,22 +104,32 @@
     assert_int_equal(0, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "-0";
+    /* integer, positive exponent */
+    str = "550E3";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0", jsonctx->value);
-    assert_int_equal(2, jsonctx->value_len);
-    assert_int_equal(0, jsonctx->dynamic);
+    assert_string_equal("550000", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    /* exp number */
-    str = "1E10";
+    str = "-550E3";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("10000000000", jsonctx->value);
-    assert_int_equal(11, jsonctx->value_len);
+    assert_string_equal("-550000", jsonctx->value);
+    assert_int_equal(7, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    /* integer, negative exponent */
+    str = "1E-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.1", jsonctx->value);
+    assert_int_equal(3, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
@@ -132,185 +142,318 @@
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "15E-2";
+    str = "-15E-1";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.15", jsonctx->value);
+    assert_string_equal("-1.5", jsonctx->value);
     assert_int_equal(4, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "-15E-2";
+    str = "16E-2";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0.15", jsonctx->value);
-    assert_int_equal(5, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "15E-3";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.015", jsonctx->value);
-    assert_int_equal(5, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "1E1000";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-
-    str = "0E0";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0", jsonctx->value);
-    assert_int_equal(1, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "0E-0";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0", jsonctx->value);
-    assert_int_equal(1, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    /* exp fraction number */
-    str = "1.1e3";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("1100", jsonctx->value);
+    assert_string_equal("0.16", jsonctx->value);
     assert_int_equal(4, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "12.1e3";
+    str = "-16E-2";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("12100", jsonctx->value);
+    assert_string_equal("-0.16", jsonctx->value);
     assert_int_equal(5, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "0.0e0";
+    str = "17E-3";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.0", jsonctx->value);
+    assert_string_equal("0.017", jsonctx->value);
+    assert_int_equal(5, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "-17E-3";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("-0.017", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "21000E-2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("210", jsonctx->value);
     assert_int_equal(3, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    /* negative exp fraction number */
-    str = "1.1e-3";
+    str = "21000E-4";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.0011", jsonctx->value);
-    assert_int_equal(6, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "12.4e-3";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.0124", jsonctx->value);
-    assert_int_equal(6, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "12.4e-2";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.124", jsonctx->value);
-    assert_int_equal(5, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "0.0e-0";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("0.0", jsonctx->value);
+    assert_string_equal("2.1", jsonctx->value);
     assert_int_equal(3, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    /* exp negative fraction number */
-    str = "-0.11e3";
+    str = "21000E-7";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-110", jsonctx->value);
-    assert_int_equal(4, jsonctx->value_len);
-    assert_int_equal(1, jsonctx->dynamic);
-    lyjson_ctx_free(jsonctx);
-
-    str = "-44.11e3";
-    assert_non_null(ly_in_memory(in, str));
-    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
-    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-44110", jsonctx->value);
+    assert_string_equal("0.0021", jsonctx->value);
     assert_int_equal(6, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "-0.0e0";
+    /* decimal number, positive exponent */
+    str = "5.087E1";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0.0", jsonctx->value);
-    assert_int_equal(4, jsonctx->value_len);
+    assert_string_equal("50.87", jsonctx->value);
+    assert_int_equal(5, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    /* negative exp negative fraction number */
-    str = "-3.14e-3";
+    str = "-5.087E1";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0.00314", jsonctx->value);
-    assert_int_equal(8, jsonctx->value_len);
+    assert_string_equal("-50.87", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "-98.7e-3";
+    str = "5.087E5";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0.0987", jsonctx->value);
+    assert_string_equal("508700", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "59.1e+1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("591", jsonctx->value);
+    assert_int_equal(3, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.005087E1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.05087", jsonctx->value);
     assert_int_equal(7, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "-98.7e-2";
+    str = "0.005087E2";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0.987", jsonctx->value);
+    assert_string_equal("0.5087", jsonctx->value);
     assert_int_equal(6, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
-    str = "-0.0e-0";
+    str = "0.005087E6";
     assert_non_null(ly_in_memory(in, str));
     assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
-    assert_string_equal("-0.0", jsonctx->value);
+    assert_string_equal("5087", jsonctx->value);
     assert_int_equal(4, jsonctx->value_len);
     assert_int_equal(1, jsonctx->dynamic);
     lyjson_ctx_free(jsonctx);
 
+    str = "0.05087E6";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("50870", jsonctx->value);
+    assert_int_equal(5, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.005087E8";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("508700", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    /* decimal number, negative exponent */
+    str = "35.94e-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("3.594", jsonctx->value);
+    assert_int_equal(5, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "-35.94e-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("-3.594", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "35.94e-2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.3594", jsonctx->value);
+    assert_int_equal(6, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "35.94e-3";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.03594", jsonctx->value);
+    assert_int_equal(7, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.3594e-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.03594", jsonctx->value);
+    assert_int_equal(7, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.03594e-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.003594", jsonctx->value);
+    assert_int_equal(8, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.003594e-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.0003594", jsonctx->value);
+    assert_int_equal(9, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.3594e-2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.003594", jsonctx->value);
+    assert_int_equal(8, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.03594e-2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.0003594", jsonctx->value);
+    assert_int_equal(9, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0.003594e-2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.00003594", jsonctx->value);
+    assert_int_equal(10, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    /* zero */
+    str = "0";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_true(jsonctx->value[0] == '0');
+    assert_int_equal(1, jsonctx->value_len);
+    assert_int_equal(0, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "-0";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_true(jsonctx->value[0] == '-');
+    assert_true(jsonctx->value[1] == '0');
+    assert_int_equal(2, jsonctx->value_len);
+    assert_int_equal(0, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "94E0";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_true(jsonctx->value[0] == '9');
+    assert_true(jsonctx->value[1] == '4');
+    assert_int_equal(2, jsonctx->value_len);
+    assert_int_equal(0, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "0E2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_true(jsonctx->value[0] == '0');
+    assert_int_equal(1, jsonctx->value_len);
+    assert_int_equal(0, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "-0E2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_true(jsonctx->value[0] == '-');
+    assert_true(jsonctx->value[1] == '0');
+    assert_int_equal(2, jsonctx->value_len);
+    assert_int_equal(0, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "5.320e+2";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("532", jsonctx->value);
+    assert_int_equal(3, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
+    str = "5.320e-1";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+    assert_string_equal("0.532", jsonctx->value);
+    assert_int_equal(5, jsonctx->value_len);
+    assert_int_equal(1, jsonctx->dynamic);
+    lyjson_ctx_free(jsonctx);
+
     /* various invalid inputs */
     str = "-x";
     assert_non_null(ly_in_memory(in, str));
@@ -352,6 +495,16 @@
     assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
     CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
 
+    str = "1E1000";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    CHECK_LOG_CTX("Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.", "Line number 1.");
+
+    str = "1e9999999999999999999";
+    assert_non_null(ly_in_memory(in, str));
+    assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+    CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1e9999999999999999999).", "Line number 1.");
+
     ly_in_free(in, 0);
 }