iio: afe: rescale: fix accuracy for small fractional scales
authorLiam Beguin <liambeguin@gmail.com>
Sun, 13 Feb 2022 02:57:33 +0000 (21:57 -0500)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 27 Feb 2022 13:38:16 +0000 (13:38 +0000)
The approximation caused by integer divisions can be costly on smaller
scale values since the decimal part is significant compared to the
integer part. Switch to an IIO_VAL_INT_PLUS_NANO scale type in such
cases to maintain accuracy.

Signed-off-by: Liam Beguin <liambeguin@gmail.com>
Reviewed-by: Peter Rosin <peda@axentia.se>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220213025739.2561834-5-liambeguin@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/afe/iio-rescale.c

index 8eaf766e28cc12a1026be84ddf4c7cb2c7188810..5d78f0cf47d28109463d760b37d1cd577f4d27db 100644 (file)
@@ -24,7 +24,7 @@ int rescale_process_scale(struct rescale *rescale, int scale_type,
                          int *val, int *val2)
 {
        s64 tmp;
-       s32 rem;
+       s32 rem, rem2;
        u32 mult;
        u32 neg;
 
@@ -43,9 +43,23 @@ int rescale_process_scale(struct rescale *rescale, int scale_type,
                tmp = (s64)*val * 1000000000LL;
                tmp = div_s64(tmp, rescale->denominator);
                tmp *= rescale->numerator;
-               tmp = div_s64(tmp, 1000000000LL);
+
+               tmp = div_s64_rem(tmp, 1000000000LL, &rem);
                *val = tmp;
-               return scale_type;
+
+               if (!rem)
+                       return scale_type;
+
+               tmp = 1 << *val2;
+
+               rem2 = *val % (int)tmp;
+               *val = *val / (int)tmp;
+
+               *val2 = rem / (int)tmp;
+               if (rem2)
+                       *val2 += div_s64((s64)rem2 * 1000000000LL, tmp);
+
+               return IIO_VAL_INT_PLUS_NANO;
        case IIO_VAL_INT_PLUS_NANO:
        case IIO_VAL_INT_PLUS_MICRO:
                mult = scale_type == IIO_VAL_INT_PLUS_NANO ? 1000000000L : 1000000L;