Re: [PATCH RFC v3 1/5] lib: kstrtox: support E notation in kstrtoudec64() and kstrtodec64()

From: Andy Shevchenko

Date: Wed Oct 21 2026 - 04:57:18 EST


On Tue, Oct 20, 2026 at 09:26:59AM -0300, Rodrigo Alencar via B4 Relay wrote:
>
> 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. Digits
> beyond the resulting precision are truncated, as before.
>
> 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 _skip_digits(const char **s)

Leading underscore is unneeded here.

...

> 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 */

So, I know that '.NNN' is valid notation, but is 'NNN.'?

> - rv_frac = _parse_integer(s, 10, &_res, scale, _res);
> - if (rv_frac & KSTRTOX_OVERFLOW)
> + s_frac = s;
> + len_frac = _skip_digits(&s);
> +
> + if (!len_int && !len_frac)
> + return -EINVAL;
> +
> + if (*s == 'e' || *s == 'E') { /* E Notation */

You may use _tolower() helper (yes, the one with a single leading underscore).

> + s++;
> + ret = kstrtoint(s, 10, &exp);
> + if (ret)
> + return ret;

So, with the above in mind, this may lead to 'NNN.e10' input which I find a bit
confusing.

> + } else { /* verify termination */
> + if (*s == '\n')
> + s++;
> + if (*s)
> + return -EINVAL;
> + }
> +
> + if (check_add_overflow(exp, (int)scale, &exp))
> return -ERANGE;
> - s += rv_frac;
>
> - /*
> - * 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))
> - return -EINVAL;
> + if (exp < 0) {
> + len_int = max(len_int + exp, 0);
> + exp = 0;
> + }
>
> - while (isdigit(*s)) /* truncate digits */
> - s++;
> + rv = _parse_integer(s_int, 10, &_res, len_int);
> + if (rv & KSTRTOX_OVERFLOW)
> + return -ERANGE;
>
> - if (*s == '\n')
> - s++;
> - if (*s)
> - return -EINVAL;
> + 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;

--
With Best Regards,
Andy Shevchenko