diff mbox series

[01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2

Message ID 20220418192907.763933-2-jic23@kernel.org (mailing list archive)
State Superseded
Headers show
Series staging/iio: Clean up AD7746 CDC driver and move from staging. | expand

Commit Message

Jonathan Cameron April 18, 2022, 7:28 p.m. UTC
From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

With some high resolution sensors such as the ad7746 the
build up of error when multiplying the _raw and _scale
values together can be significant.  Reduce this affect by
providing additional resolution in both calculation and
formatting of result.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/iio/industrialio-core.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

Comments

Peter Rosin April 26, 2022, noon UTC | #1
Hi!

> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 
> With some high resolution sensors such as the ad7746 the
> build up of error when multiplying the _raw and _scale
> values together can be significant.  Reduce this affect by
> providing additional resolution in both calculation and
> formatting of result.
> 
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  drivers/iio/industrialio-core.c | 23 +++++++++++++++--------
>  1 file changed, 15 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 2f48e9a97274..d831835770da 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -683,14 +683,21 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
>  		else
>  			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
>  					     abs(tmp1));
> -	case IIO_VAL_FRACTIONAL_LOG2:
> -		tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
> -		tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1);
> -		if (tmp0 == 0 && tmp2 < 0)
> -			return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1));
> -		else
> -			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
> -					     abs(tmp1));
> +	case IIO_VAL_FRACTIONAL_LOG2: {
> +		u64 t1, t2;
> +		int integer;
> +		bool neg = vals[0] < 0;
> +
> +		t1 = shift_right((u64)abs(vals[0]) * 1000000000000ULL, vals[1]);

While the multiplication is safe if val[0] is 24 bits or less, I
wonder if that's OK for *all* the existing stuff? I would have guessed
that something somewhere needs more bits than that? Did you consider the
rescaler? And if everything currently really does fit in 24 bits, do we
really want to make it difficult to add something beyond 24 bits later
on?

Cheers,
Peter

> +		integer = (int)div64_u64_rem(t1, 1000000000000ULL, &t2);
> +		if (integer == 0 && neg)
> +			return sysfs_emit_at(buf, offset, "-0.%012llu", abs(t2));
> +		if (neg)
> +			integer *= -1;
> +		return sysfs_emit_at(buf, offset, "%d.%012llu", integer,
> +				     abs(t2));
> +		}
> +	}
>  	case IIO_VAL_INT_MULTIPLE:
>  	{
>  		int i;
> -- 
> 2.35.3
>
Jonathan Cameron April 28, 2022, 6:49 p.m. UTC | #2
On Tue, 26 Apr 2022 14:00:49 +0200
Peter Rosin <peda@axentia.se> wrote:

> Hi!
> 
> > From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > 
> > With some high resolution sensors such as the ad7746 the
> > build up of error when multiplying the _raw and _scale
> > values together can be significant.  Reduce this affect by
> > providing additional resolution in both calculation and
> > formatting of result.
> > 
> > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > ---
> >  drivers/iio/industrialio-core.c | 23 +++++++++++++++--------
> >  1 file changed, 15 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index 2f48e9a97274..d831835770da 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -683,14 +683,21 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
> >  		else
> >  			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
> >  					     abs(tmp1));
> > -	case IIO_VAL_FRACTIONAL_LOG2:
> > -		tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
> > -		tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1);
> > -		if (tmp0 == 0 && tmp2 < 0)
> > -			return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1));
> > -		else
> > -			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
> > -					     abs(tmp1));
> > +	case IIO_VAL_FRACTIONAL_LOG2: {
> > +		u64 t1, t2;
> > +		int integer;
> > +		bool neg = vals[0] < 0;
> > +
> > +		t1 = shift_right((u64)abs(vals[0]) * 1000000000000ULL, vals[1]);  
> 
> While the multiplication is safe if val[0] is 24 bits or less, I
> wonder if that's OK for *all* the existing stuff? I would have guessed
> that something somewhere needs more bits than that? Did you consider the
> rescaler? And if everything currently really does fit in 24 bits, do we
> really want to make it difficult to add something beyond 24 bits later
> on?

Good point. Perhaps we can do something a bit nefarious and check that
val[0] is sufficiently small and if not fallback to lower precision
as we had before?  Or if adventurous adjust the precision automatically
so we can get as many digits (at least 9) as can be computed without
overflow...  For now, I think 2 steps is enough though.

Jonathan
 
> 
> Cheers,
> Peter
> 
> > +		integer = (int)div64_u64_rem(t1, 1000000000000ULL, &t2);
> > +		if (integer == 0 && neg)
> > +			return sysfs_emit_at(buf, offset, "-0.%012llu", abs(t2));
> > +		if (neg)
> > +			integer *= -1;
> > +		return sysfs_emit_at(buf, offset, "%d.%012llu", integer,
> > +				     abs(t2));
> > +		}
> > +	}
> >  	case IIO_VAL_INT_MULTIPLE:
> >  	{
> >  		int i;
> > -- 
> > 2.35.3
> >   
>
diff mbox series

Patch

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 2f48e9a97274..d831835770da 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -683,14 +683,21 @@  static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
 		else
 			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
 					     abs(tmp1));
-	case IIO_VAL_FRACTIONAL_LOG2:
-		tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]);
-		tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1);
-		if (tmp0 == 0 && tmp2 < 0)
-			return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1));
-		else
-			return sysfs_emit_at(buf, offset, "%d.%09u", tmp0,
-					     abs(tmp1));
+	case IIO_VAL_FRACTIONAL_LOG2: {
+		u64 t1, t2;
+		int integer;
+		bool neg = vals[0] < 0;
+
+		t1 = shift_right((u64)abs(vals[0]) * 1000000000000ULL, vals[1]);
+		integer = (int)div64_u64_rem(t1, 1000000000000ULL, &t2);
+		if (integer == 0 && neg)
+			return sysfs_emit_at(buf, offset, "-0.%012llu", abs(t2));
+		if (neg)
+			integer *= -1;
+		return sysfs_emit_at(buf, offset, "%d.%012llu", integer,
+				     abs(t2));
+		}
+	}
 	case IIO_VAL_INT_MULTIPLE:
 	{
 		int i;