linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/1] iio: core: Improve precision of __iio_format_value for FRACTIONAL values
@ 2019-01-29  6:26 Phil Reid
  2019-01-29  8:32 ` Alexandru Ardelean
  0 siblings, 1 reply; 6+ messages in thread
From: Phil Reid @ 2019-01-29  6:26 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, preid, linux-iio

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@electromag.com.au>
---

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


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-02-01 11:22 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-29  6:26 [PATCH 1/1] iio: core: Improve precision of __iio_format_value for FRACTIONAL values Phil Reid
2019-01-29  8:32 ` Alexandru Ardelean
2019-01-29  9:11   ` Phil Reid
2019-01-31 13:35     ` Jonathan Cameron
2019-02-01  5:47       ` Phil Reid
2019-02-01 11:22         ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).