1

I am doing 32bit unsigned multiplication of two fixed point integers like below:

888.88 x 805.00 = 7,155,484,000 (greater then 32 bit)

But i need the result like below:
888.88 x 805.00 = 715,548.4

I am doing this in PIC16 Assembly language, I don't want to use Floating Point Routine due to Program memory limitation. Is there is any other way or trick to get the required result within 32 bits?

I need help in this issue! Please answer if possible.

9
  • 1
    You need a wider product before you right shift to renormalize the fixed-point result, e.g. do the high x high cross products to get a 32 x 32 => 64-bit widening multiply. If one of the inputs is known to always have its fractional bits being zero, you can right-shift it before multiplying, so the mul result is the final fixed-point result directly. (If it's a known constant, you can just use 805 instead of 805<<8 or whatever.) Commented Aug 7, 2023 at 17:27
  • I am multiplying 32x32bit and then I got result in 64 bit wider product as you said, but my fractional parts is not constant and will change any time, can you give any example for better undestanding? Commented Aug 8, 2023 at 10:48
  • Short version of 32 bit Floating Point Routines in PIC16 assembly is long only about 256 instructions. Is this size too much for you? Commented Aug 8, 2023 at 14:22
  • I am working on PIC16f628a and program memory is only 2K, I need solution in Fixed point arithmetic. Commented Aug 8, 2023 at 15:12
  • After getting a 64-bit integer product, right-shifting to renormalize before truncating to 32-bit fixed-point avoids truncation. You do of course need an extended-precision shift. Commented Aug 8, 2023 at 15:48

1 Answer 1

1

The question does not specify a fixed-point format. I will assume a binary fixed-point format, rather than a decimal one possibly based on BCD encoding. Since the example shows numbers with two decimals and the question specifies unsigned operands, I choose UQ24.8 format mapped to a uint32_t, with 24 bits allocated to the integer portion and 8 bits allocated to the fraction portion.

Each fixed-point number contains an implicit scale factor based on the number of fraction bits. For a UQ24.8 operand this scale factor is 28 = 256. When we use integer multiplication to affect fixed-point multiplication, the scale factor is included in the product twice, so we need to divide by the scale factor to get the desired UQ24.8 result with the scale factor applied once. For binary fixed-point arithmetic that division is simply a right shift by the number of fraction bits.

We can also round the result of the multiplication by adding half of the scale factor prior to the division. In my experience the overhead of a rounded fixed-point multiplication is low enough on most platforms to make it worthwhile by preventing numerical drift in computations due to the biased error caused by truncation.

I am not familiar with PIC assembly language, but the ISO-C99 code below demonstrates the principle. The output of this little program should look like this:

factor             a = 000378e1    888.879
factor             b = 00032503    805.012
truncated product: p = 0aeb25ef 715557.934
rounded product:   p = 0aeb25f0 715557.938

For decimal fixed-point arithmetic the mechanics of fixed-point multiplication are the same, except that the scale factor is a power of 10, requiring a relatively expensive division rather than a simple extended-width right shift. However, this division is by a constant so it can be replaced with multiplication with the reciprocal, something that any optimizing compiler should know how to do, as a general algorithm has been known since the 1990s.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>

#define UQ24P8_FRACT_BITS (8)
#define UQ24P8_INTGR_BITS (24)
#define UQ24P8_SCALE_FACT (1L << UQ24P8_FRACT_BITS)
#define UQ24P8_OVFL_LIMIT (1L << UQ24P8_INTGR_BITS)

typedef uint32_t uq24p8;

uq24p8 mul_uq24p8 (uq24p8 a, uq24p8 b, int round)
{
    uint64_t p = ((uint64_t)a) * b;
    if (!round) {
        return (uq24p8)(p / UQ24P8_SCALE_FACT); // p >> UQ24P8_FRACT_BITS
    } else {
        return (uq24p8)((p + (UQ24P8_SCALE_FACT / 2)) / UQ24P8_SCALE_FACT);
    }
}

uq24p8 float_to_uq24p8 (float a)
{
    float t = roundf (a * UQ24P8_SCALE_FACT);
    if ((t < 0) || (t >= UQ24P8_OVFL_LIMIT)) {
        fprintf (stderr, "float_to_uq24p8: conversion error\n");
    }
    return ((uq24p8)t);
}

double uq24p8_to_double (uq24p8 a)
{
    return ((double)a) / UQ24P8_SCALE_FACT;
}

int main (void)
{
    uq24p8 a = float_to_uq24p8 (888.88f);
    uq24p8 b = float_to_uq24p8 (805.01f);
    uq24p8 p;

    printf ("factor             a = %08" PRIx32 " %10.3f\n", a, uq24p8_to_double(a));
    printf ("factor             b = %08" PRIx32 " %10.3f\n", b, uq24p8_to_double(b));
    p = mul_uq24p8 (a, b, 0);
    printf ("truncated product: p = %08" PRIx32 " %10.3f\n", p, uq24p8_to_double(p));
    p = mul_uq24p8 (a, b, 1);
    printf ("rounded product:   p = %08" PRIx32 " %10.3f\n", p, uq24p8_to_double(p));
    return EXIT_SUCCESS;
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for detailed answer, If I don't want to use float and double then what will be the change in code? Simply I want to use uint32_t
@AdilAhmed Only the test scaffolding in the above code uses floating-point types. The actual multiplication code, mul_uq24p8(), does not.
Ok I understand, but why the result of multiplication is not accurate? I mean 888.879 X 805.012 = 715557.934 or 715557.938, but in Calculator it gives 715,558.261548
@AdilAhmed Generally, there will be a loss of information in multiplication. Multiplying two n-bit quantities produces a result comprising 2n bits. In order to get back to an n-bit representation, we get rid of the least significant n bits of the product (by truncation or rounding).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.