segunda-feira, 25 de maio de 2020

Representação numérica em BASIC (IV) - virgula flutuante

Este artigo sobre virgula flutuante, é acerca de como nela armazenar um número na representação de 5 bytes. Vamos reproduzir o texto do manual do Spectrum:

From the ZX Spectrum BASIC Manual:

Any number (except 0) can be written uniquely as

 ± m x 2e where
 ± is the sign,
m is the mantissa, and lies between 0.5 and 1 (it cannot be 1),
and e is the exponent, a whole number (possibly negative).

Suppose you write m in the binary scale. Because it is a fraction, it will have a binary point (like the decimal point in the scale of ten) and then a binary fraction (like a decimal fraction); so in binary:
a half is written .1
a quarter is written .01
three quarters is written .11
a tenth is written .000110011001100110011 ... and so on.

With our number m, because it is less than 1, there are no bits before the binary point, and because it is at least 0.5, the bit immediately after the binary point is a 1.

To store the number in the computer, we use five bytes, as follows:

I. write the first eight bits of the mantissa in the second byte (we know that the first bit is 1), the second eight bits in the third byte, the third eight bits in the fourth byte and the fourth eight bits in the fifth byte;
II. replace the first bit in the second byte which we know is 1 by the sign: 0 for plus, 1 for minus;
III. write the exponent +128 in the first byte. For instance, suppose our number is 1/10
1/10 =4/5x2-3

Thus the mantissa m is .11001100110011001100110011001100 in binary (since the 33rd bit is 1, we shall round the 32nd up from 0 to 1), and the exponent e is 3.

Applying our three rules gives the five bytes

Fica aqui o programa em C, para ajudar à compreensão:

#define Z_UNDERFLOW -2
#define Z_OVERFLOW -1

int d2zx(unsigned char *out, double in) {
long mantissa;
bool sign = in < 0;

if(sign) in = -in;
out[0]=0x80;

// expoente negativo
 while(in < 0.5) {
in *= 2.0;
out[0]--;
if(!out[0]) return Z_UNDERFLOW;
}

// expoente positivo
while(in >= 1) {             
in *= 0.5;                    
out[0]++;
if(!out[0]) return Z_OVERFLOW;
}

in *= 0x100000000l; // máximo tamanho mantissa + 1 (33 bytes)
in += 0.5;          // rounding 
mantissa = in;

 // 32 bits distribuídos em 4 bytes
out[1] = mantissa >> 24;
mantissa &= 0xFFFFFFl;
out[2] = mantissa >> 16;
mantissa &= 0xFFFFl;
out[3] = mantissa >> 8;
mantissa &= 0xFFl;  
out[4] = mantissa;

if(!sign) out[1] &= 0x7F; // se positivo, bit esquerdo da mantissa
                           // zerado
return 0;
}

Ver: Binary numbers – floating point conversion

Sem comentários:

Publicar um comentário