Q (formato de número) - Q (number format)

A notação Q é uma maneira sucinta de especificar os parâmetros de um formato de número de ponto fixo binário . Uma série de outras notações foram usadas para o mesmo propósito.

Definição

Versão Texas Instruments

A notação Q, conforme definida pela Texas Instruments, consiste na letra Qseguida por um par de números m .n , onde m é o número de bits usados ​​para a parte inteira do valor en é o número de bits de fração.

Por padrão, a notação descreve assinado formato binário ponto fixo, com o inteiro fora de escala a ser armazenado em dois complemento formato, usado na maioria dos processadores binários. O primeiro bit sempre dá o sinal do valor (1 = negativo, 0 = não negativo), e que é não contadas no m parâmetro. Assim, o número total w de bits usados ​​é 1 + m + n .

Por exemplo, a especificação Q3.12descreve um número de ponto fixo binário com sinal com w = 16 bits no total, compreendendo o bit de sinal, três bits para a parte inteira e 12 bits que são considerados fração. Ou seja, um inteiro assinado de 16 bits (complemento de dois), que é implicitamente multiplicado pelo fator de escala 2 −12

Em particular, quando n é zero, os números são apenas inteiros -. Se m for zero, todos os bits, exceto o bit de sinal, são bits de fração; então, o intervalo do número armazenado é de -1,0 (inclusivo) a +1 (exclusivo). Ambos m e n podem ser negativos

O meo ponto podem ser omitidos, caso em que são inferidos a partir do tamanho da variável ou registro onde o valor está armazenado. Portanto, Q12significa um inteiro com sinal com qualquer número de bits, que é implicitamente multiplicado por 2 −12 .

A letra Upode ser prefixada a Qpara denotar um formato de ponto fixo binário sem sinal . Por exemplo, UQ1.15descreve valores representados como inteiros de 16 bits sem sinal com fator de escala implícito de 2 −15 , que varia de 0,0 a (2 16 -1) / 2 15 = +1,999969482421875.

Versão AMD

Uma variante da notação Q tem sido usada pela AMD . Nesta variante, o número m inclui o bit de sinal. Por exemplo, um inteiro assinado de 16 bits seria denotado Q15.0na variante TI, mas Q16.0na variante AMD.

Características

A resolução (diferença entre valores sucessivos) de um Q m . n ou UQ m . O formato n (ambos usaram a convenção AMD) é sempre 2 - n . O intervalo de valores representáveis ​​é

  • −2 m −1 a +2 m −1 - 2 - n para formato assinado, e
  • 0 a 2 m - 2 - n para o formato sem sinal.

Por exemplo, um número de formato Q15.1 requer 15 + 1 = 16 bits, tem resolução 2 −1 = 0,5 e os valores representáveis ​​variam de -2 14 = -16384,0 a +2 14 - 2 −1 = +16383,5. Em hexadecimal, os valores negativos variam de 0x8000 a 0xFFFF seguidos pelos não negativos de 0x0000 a 0x7FFF.

Operações matemáticas

Os números Q são uma proporção de dois inteiros: o numerador é mantido no armazenamento, o denominador é igual a 2 n .

Considere o seguinte exemplo:

  • O denominador Q8 é igual a 2 8 = 256
  • 1,5 é igual a 384/256
  • 384 é armazenado, 256 é inferido porque é um número Q8.

Se a base do número Q deve ser mantida ( n permanece constante), as operações matemáticas do número Q devem manter o denominador constante. As fórmulas a seguir mostram operações matemáticas nos números Q gerais e .

Como o denominador é uma potência de dois, a multiplicação pode ser implementada como um deslocamento aritmético para a esquerda e a divisão como um deslocamento aritmético para a direita; em muitos processadores, as mudanças são mais rápidas do que a multiplicação e a divisão.

Para manter a precisão, os resultados intermediários da multiplicação e divisão devem ter precisão dupla e deve-se tomar cuidado ao arredondar o resultado intermediário antes de convertê-lo de volta ao número Q desejado.

Usando C, as operações são (observe que aqui, Q se refere ao número de bits da parte fracionária):

Adição

int16_t q_add(int16_t a, int16_t b)
{
    return a + b;
}

Com saturação

int16_t q_add_sat(int16_t a, int16_t b)
{
    int16_t result;
    int32_t tmp;

    tmp = (int32_t)a + (int32_t)b;
    if (tmp > 0x7FFF)
        tmp = 0x7FFF;
    if (tmp < -1 * 0x8000)
        tmp = -1 * 0x8000;
    result = (int16_t)tmp;

    return result;
}

Ao contrário do ponto flutuante ± Inf, os resultados saturados não são pegajosos e não ficarão saturados ao adicionar um valor negativo a um valor saturado positivo (0x7FFF) e vice-versa na implementação mostrada. Em linguagem assembly, o sinalizador Signed Overflow pode ser usado para evitar as previsões de tipos necessárias para essa implementação C.

Subtração

int16_t q_sub(int16_t a, int16_t b)
{
    return a - b;
}

Multiplicação

// precomputed value:
#define K   (1 << (Q - 1))
 
// saturate to range of int16_t
int16_t sat16(int32_t x)
{
	if (x > 0x7FFF) return 0x7FFF;
	else if (x < -0x8000) return -0x8000;
	else return (int16_t)x;
}

int16_t q_mul(int16_t a, int16_t b)
{
    int16_t result;
    int32_t temp;

    temp = (int32_t)a * (int32_t)b; // result type is operand's type
    // Rounding; mid values are rounded up
    temp += K;
    // Correct by dividing by base and saturate result
    result = sat16(temp >> Q);

    return result;
}

Divisão

int16_t q_div(int16_t a, int16_t b)
{
    /* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */
    int32_t temp = (int32_t)a << Q;
    /* Rounding: mid values are rounded up (down for negative values). */
    /* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */
    if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0)) {   
        temp += b / 2;    /* OR shift 1 bit i.e. temp += (b >> 1); */
    } else {
        temp -= b / 2;    /* OR shift 1 bit i.e. temp -= (b >> 1); */
    }
    return (int16_t)(temp / b);
}

Veja também

Referências

Leitura adicional

links externos