Search code examples
floating-pointfixed-point

float to fixed conversion with different scaling factors


Can anyone please let me know What will be the difference between these approcahes when I convert fixed to float and float to fixed.

a) int a=32767; float b = 32765*(1/32767) // 16 bit scaling factor int c = b*32767;

b) int a=32767; float b = 32765*(1/1.0) // scaling factor=1 int c = b*1;

a) int a=32767; float b = 32765*(1/0x80000) // 24 bit scaling factor int c = b*(0x80000);

If my machine uses Q23 fixed point representation, which should I use ?


Solution

  • I didn't find any detailed information about the "Q23 fixed point representation" that your machine uses, so I made up my own definition, wrote some conversion routines and tested them for some few values:

    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    /**
     * q23 is a fixed-point two's-complement type:
     * - 1 bit sign,
     * - 8 bit integer part,
     * - 23 bit fraction part.
     *
     * long is assumed to be 32 bit two's complement without padding bits.
     */
    typedef long q23;
    
    static q23
    cvt_float_to_q23(float f) {
      return f * (1 << 23);
    }
    
    static float
    cvt_q23_to_float(q23 x) {
      return ((float) x) / (1 << 23);
    }
    
    /*
     * Here starts the testing code.
     */
    
    static unsigned errors = 0;
    
    static void
    assert_q23_is(q23 fixed, float f) {
      if (cvt_q23_to_float(fixed) != f) {
        fprintf(stderr, "E: cvt_q23_to_float(%08lx) expected %.10f, got %.10f\n",
            fixed, f, cvt_q23_to_float(fixed));
        errors++;
      }
    }
    
    static void
    assert_float_is(float f, q23 fixed) {
      if (cvt_float_to_q23(f) != fixed) {
        fprintf(stderr, "E: cvt_float_to_q23(%f) expected %08lx, got %08lx\n",
            f, fixed, cvt_float_to_q23(f));
        errors++;
      }
    }
    
    static void
    assert_equals(q23 fixed, float f) {
      assert_q23_is(fixed, f);
      assert_float_is(f, fixed);
    }
    
    int
    main() {
      /* Some values have equivalent representation. */
      assert_equals(LONG_MIN, -256.0f);
      assert_equals(LONG_MIN / 2, -128.0f);
      assert_equals(0, 0.0f);
      assert_equals(LONG_MAX / 2 + 1, 128.0f);
    
      /* There will be a fixpoint ... */
      assert_float_is(1.0 / 3, 0x002aaaaa);
      assert_q23_is(0x002aaaaa, 0.3333332539f);
    
      /* float only has 24 bits of precision */
      assert_equals(0x2aaaaac0, 256.0 / 3);
    
      if (errors == 0) {
        printf("ok\n");
        return EXIT_SUCCESS;
      }
      return EXIT_FAILURE;
    }
    

    Some remarks:

    • If you need saturated rounding you have to implement that yourself by checking the argument to cvt_float_to_q23.
    • There will be rounding errors everywhere, and they are inevitable. Be sure to handle them appropriately.