Wednesday, August 31, 2022

[SOLVED] difference between _Fract , _Sat and _Accum in GCC and why to use Fixed-point types?

Issue

I was reading GCC documentation on Fixed-point types from this link , but it seems GCC is not Elaborating on so many things which left me with many questions in my head

  1. first of all they, said

The fixed-point types are short _Fract, _Fract, long _Fract, long long _Fract, unsigned short _Fract, unsigned _Fract, unsigned long _Fract, unsigned long long _Fract, _Sat short _Fract, _Sat _Fract, _Sat long _Fract, _Sat long long _Fract, _Sat unsigned short _Fract, _Sat unsigned _Fract, _Sat unsigned long _Fract, _Sat unsigned long long _Fract, short _Accum, _Accum, long _Accum, long long _Accum, unsigned short _Accum, unsigned _Accum, unsigned long _Accum, unsigned long long _Accum, _Sat short _Accum, _Sat _Accum, _Sat long _Accum, _Sat long long _Accum, _Sat unsigned short _Accum, _Sat unsigned _Accum, _Sat unsigned long _Accum, _Sat unsigned long long _Accum.

so what are the differences between

  • _Fract
  • _Sat
  • _Accum

since the fixed point types are just combination between primitive data type and the above 3 key works ,so what is the difference when I write short _Fract x; or short _Accum x; ? and what does keyword _Sat add to the definition of the variable ? like what is the difference between _Sat _Fract x; and _Sat _Accum x; ? as GCC didn't state anything about this thing ,

also they said:

Not all targets support fixed-point types.

so I couldn't test anything from the above types on my host machines as it gives me error stating that fixed-point types not supported for this target

  1. I have read about Decimal , float and Fixed-Point types

all of the above are just different representations of decimal with fraction numbers , from my understand they are just like:

  • Decimal representation gives lower accuracy but higher range with big numbers , most 2 used standards are binary integer decimal (BID) and densely packed decimal (DPD)
  • float and doubles gives higher accuracy than Decimal when talking about big numbers but lower range ,they follow IEEE 754 standard
  • Fixed-Point types have the lowest range but they are the most accurate one

please correct me if I am wrong on the above 3 lines , so my question is as follow , in the narrow range of small numbers like when we are talking about Fixed-Point types , they provide high accuracy but also Decimals can be used instead of them in that small range and Decimals will provide same accuracy as nearly as Fixed-Point types , Decimals only lacks accuracy when numbers become big , so why we use Fixed-Point types ? as it will give us only small range of numbers and decimal can replace it in terms of accuracy in that small range of numbers.


Solution

See https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1169.pdf and https://en.wikipedia.org/wiki/Q_(number_format) .

what is the difference when I write short _Fract x; or short _Accum x; ?

short _Fract would be a variable 1 sign bit and 15 fraction bits, short _Accum would be a variable with 1 sign bit, 8 integer bits and 7 fraction bits. (Most commonly)

Different compilers have different sizes: https://onlinedocs.microchip.com/pr/GUID-C4E60FF5-3DAB-44F1-BA61-4BD962D8F469-en-US-1/index.html?GUID-8856FDD7-C356-499B-9B39-8F7304854058 . I saw such a list for GCC of all fixed-point types, but I can't find it.

what is the difference between them ?

The difference is in the representation, which translates into a different range and resolution of representable values. They have different count of integer and fraction bits.

what does keyword _Sat add to the definition of the variable ?

_Sat comes from saturating fixed-point type. When you do UINT_MAX + 1 it becomes 0, because it wraps around. In pseudocode, like (_Sat UINT_MAX) + 1 would be equal to UINT_MAX. It "saturates", not wraps around.

fixed-point types not supported for this target

So use a different target. For example, the following program:

#include <stdfix.h>
#include <stdio.h>
int main() {
    _Fract unsigned a = UFRACT_MAX;
    _Fract unsigned b = a + 0.1;
    _Sat _Fract unsigned c = UFRACT_MAX;
    _Sat _Fract unsigned d = c + 0.1;
    printf("a=%f a+0.1=%f\n", (float)a, (float)b);  // b "wrapped around"
    printf("c=%f c+0.1=%f\n", (float)c, (float)d);  // d is "capped to max"
}

Can be executed like the following:

$ arm-none-eabi-gcc -ufloat_print --specs=rdimon.specs 1.c && qemu-arm ./a.out 
a=0.999985 a+0.1=0.099976
c=0.999985 c+0.1=0.999985

why we use Fixed-Point types ?

Speed. Calculating float * float is super hard and slow. Calculating int * int is superfast. Calculating fixed-point * fixed-point is exactly as fast as int * int.



Answered By - KamilCuk
Answer Checked By - Cary Denson (WPSolving Admin)