[PATCH v4 1/4] iio: prepare iio_format_value to handle higher-precision IIO_VAL_DECIMAL64_* formats

From: Victor Chernyakin

Date: Sat Oct 31 2026 - 11:15:07 EST


Prepare iio_format_value for an upcoming patch that adds formats up
to IIO_VAL_DECIMAL64_YOCTO (a scale of 24 digits) by avoiding
computing 10^scale when doing so would overflow, and by avoiding
undefined behavior due to abs(LLONG_MIN) (which couldn't happen
previously).

Signed-off-by: Victor Chernyakin <chernyakin.victor.j@xxxxxxxxxxx>
---
drivers/iio/industrialio-core.c | 24 ++++++++++++++++++++----
include/linux/iio/iio.h | 3 +++
2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 767a7794624a..42e92a79d1ee 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -653,6 +653,16 @@ int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix)
}
EXPORT_SYMBOL(iio_read_mount_matrix);

+/*
+ * Regular abs(LLONG_MIN) causes signed overflow, which is undefined
+ * behavior. This variant avoids that by performing the computation in an
+ * unsigned type, for which negation is always well-defined.
+ */
+static inline u64 abs_s64_no_overflow(s64 x)
+{
+ return x < 0 ? -(u64)x : (u64)x;
+}
+
static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
int size, const int *vals)
{
@@ -720,13 +730,19 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
int scale = type - IIO_VAL_DECIMAL64_BASE;
s64 frac;

- tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]),
- int_pow(10, scale), &frac);
+ if (scale <= IIO_INTLOG10_S64_MAX) {
+ tmp2 = div64_s64_rem(iio_val_s64_compose(vals[0], vals[1]),
+ int_pow(10, scale), &frac);
+ } else {
+ tmp2 = 0;
+ frac = iio_val_s64_compose(vals[0], vals[1]);
+ }
+
if (tmp2 == 0 && frac < 0)
l += sysfs_emit_at(buf, offset, "-");

- l += sysfs_emit_at(buf, offset + l, "%lld.%0*lld", tmp2, scale,
- abs(frac));
+ l += sysfs_emit_at(buf, offset + l, "%lld.%0*llu", tmp2, scale,
+ abs_s64_no_overflow(frac));
return l;
}
default:
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 711c00f67371..7cb72ae282b6 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -1132,4 +1132,7 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
*/
#define IIO_M_S_2_TO_G(ms2) (((ms2) * 100000ULL + 980665ULL / 2) / 980665ULL)

+#define IIO_INTLOG10_S64_MAX 18
+#define IIO_INTLOG10_U64_MAX 19
+
#endif /* _INDUSTRIAL_IO_H_ */
--
2.53.0