Re: [PATCH v4 1/6] lib: kstrtox: support E notation in kstrtoudec64() and kstrtodec64()
From: Rodrigo Alencar
Date: Tue Oct 27 2026 - 06:23:46 EST
On 26/10/26 08:54, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@xxxxxxxxxx>
>
> Allow decimal strings to carry an exponent suffix, such as "2.7e10" or
> "271828e-5", in kstrtoudec64() and kstrtodec64(). The exponent is added
> to the requested scale to work out how many integer and fractional digits
> are consumed. A negative effective exponent drops integer digits.
>
> The digits are now scanned first and the string termination is checked
> before any conversion. A missing or malformed exponent ("1e", "1e+")
> returns -EINVAL. An exponent that overflows when added to the scale, or
> that makes the result exceed 64 bits, returns -ERANGE.
...
> static int _kstrtoudec64(const char *s, unsigned int scale, u64 *res)
> {
> - unsigned int rv_int, rv_frac;
> + const char *s_int, *s_frac;
> + int len_int, len_frac;
> + unsigned int rv;
> u64 _res = 0;
> + int exp = 0;
> + int ret;
>
> - rv_int = _parse_integer(s, 10, &_res);
> - if (rv_int & KSTRTOX_OVERFLOW)
> - return -ERANGE;
> - s += rv_int;
> + s_int = s;
> + len_int = skip_digits(&s);
>
> if (*s == '.')
> s++; /* skip decimal point */
>
> - rv_frac = _parse_integer(s, 10, &_res, scale, _res);
previous implementation required scale >= 0.
> - if (rv_frac & KSTRTOX_OVERFLOW)
> - return -ERANGE;
> - s += rv_frac;
> + s_frac = s;
> + len_frac = skip_digits(&s);
>
> - /*
> - * Check input beyond rv_int and rv_frac to cover cases like ".5" with
> - * scale 0, which is considered a valid input, being parsed as 0.
> - */
> - if (!rv_int && !rv_frac && !isdigit(*s))
> + if (!len_int && !len_frac)
> return -EINVAL;
>
> - while (isdigit(*s)) /* truncate digits */
> + if (_tolower(*s) == 'e') { /* E Notation */
> s++;
> + ret = kstrtoint(s, 10, &exp);
> + if (ret)
> + return ret;
> + } else { /* verify termination */
> + if (*s == '\n')
> + s++;
> + if (*s)
> + return -EINVAL;
> + }
>
> - if (*s == '\n')
> - s++;
> - if (*s)
> - return -EINVAL;
> + if (check_add_overflow(exp, (int)scale, &exp))
I was wondering, and I figured that the 'scale' parameter can be made
as a signed int. This E notation implementation allows for that as
the scale now folds into the exponent value. A negative scale would
make sense to allow the representation of values greater than 2^64,
still limitted to the 64-bit precision, i.e., 10^24 would fit 64-bit with
a scale of -6, which would have least significant digits ignored. The code
is ready as is, that would be an small change on a interface internal to
the kernel that wont change consumers code. More test cases would be added.
Although the absolute value of scale needs to be reasonable, the int casting
here is also not ok. big unsigned integers here might lead to negative values
without warning.
> + return -ERANGE;
> +
> + if (exp < 0) {
> + len_int = max(len_int + exp, 0);
> + exp = 0;
> + }
> +
> + rv = _parse_integer(s_int, 10, &_res, len_int);
> + if (rv & KSTRTOX_OVERFLOW)
> + return -ERANGE;
> +
> + rv = _parse_integer(s_frac, 10, &_res, exp, _res);
> + if (rv & KSTRTOX_OVERFLOW)
> + return -ERANGE;
>
> - if (_res && ((scale - rv_frac) > 19 /* log10(2^64) = 19.26 */ ||
> - check_mul_overflow(_res, int_pow(10, scale - rv_frac), &_res)))
> + exp -= rv;
> + if (_res && (exp > 19 /* log10(2^64) = 19.26 */ ||
> + check_mul_overflow(_res, int_pow(10, exp), &_res)))
> return -ERANGE;
>
> *res = _res;
--
Kind regards,
Rodrigo Alencar