[PATCH 1/1] iio: core: Improve precision of __iio_format_value for FRACTIONAL values

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Currently FRACTIONAL values are outputed with 9 digits after the decimal
place. This is not always sufficient to resolve the raw value with 1 bit.
Output FRACTIONAL values to 15 decimal places of precision, regardless
of the number of leading zeros.

Currently for a 2.5V ref with 24 bits of precision the code outputs only
to 9 decimal places.

Cur: 0.00014901100000000000 * 16777216 = 2499.989733
New: 0.00014901161193847600 * 16777216 = 2500.000000
Signed-off-by: Phil Reid <preid@xxxxxxxxxxxxxxxxx>
---

Notes:
    Alternatively I could add additonal FRACTIONAL types that select the new
    behaviour to prevent any possible regressions.

 drivers/iio/industrialio-core.c | 55 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index a062cfd..bd9da64 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -571,11 +571,53 @@ int of_iio_read_mount_matrix(const struct device *dev,
 #endif
 EXPORT_SYMBOL(of_iio_read_mount_matrix);
 
+static ssize_t __iio_format_div_prec(char *buf, unsigned int len, s64 x, s32 y)
+{
+	unsigned int prec = 0;
+	unsigned int idx = 0;
+	s64 d;
+
+	if (!len)
+		return 0;
+
+	if (!y)
+		return snprintf(buf, len, "inf");
+
+	if (!x)
+		return snprintf(buf, len, "0");
+
+	if (((x > 0) && (y < 0)) || ((x < 0) && (y > 0))) {
+		buf[idx++] = '-';
+		x = x > 0 ? x : -x;
+		y = y > 0 ? y : -y;
+	}
+
+	d = div64_s64(x, y);
+	idx += snprintf(buf+idx, len-idx, "%d", (int)d);
+	x = x - (y * d);
+	if ((x != 0) && (idx < len-1)) {
+		buf[idx++] = '.';
+		x = x * 10;
+		d = div64_s64(x, y);
+
+		while ((idx < len-1) && (prec < 15)) {
+			if (d || prec)
+				prec++;
+			buf[idx++] = '0' + (char)d;
+			x = x - (y * d);
+			if (!x)
+				break;
+			x = x * 10;
+			d = div64_s64(x, y);
+		}
+		buf[idx] = 0;
+	}
+	return idx;
+}
+
 static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
 				  int size, const int *vals)
 {
-	unsigned long long tmp;
-	int tmp0, tmp1;
 	bool scale_db = false;
 
 	switch (type) {
@@ -598,14 +640,9 @@ static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
 		else
 			return snprintf(buf, len, "%d.%09u", vals[0], vals[1]);
 	case IIO_VAL_FRACTIONAL:
-		tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
-		tmp1 = vals[1];
-		tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1);
-		return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1));
+		return __iio_format_div_prec(buf, len, vals[0], vals[1]);
 	case IIO_VAL_FRACTIONAL_LOG2:
-		tmp = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
-		tmp0 = (int)div_s64_rem(tmp, 1000000000LL, &tmp1);
-		return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1));
+		return __iio_format_div_prec(buf, len, vals[0], 1 << vals[1]);
 	case IIO_VAL_INT_MULTIPLE:
 	{
 		int i;
-- 
1.8.3.1




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux