Ir para o conteúdo
  • Revista PROGRAMAR: Já está disponível a edição #59 da revista programar. Faz já o download aqui!

Gman9

Provar que não há perda de precisão de float para short

Mensagens Recomendadas

Gman9    0
Gman9

Boas pessoal tenho este problema para resolver mas não sei bem por onde começar, alguma ideia?

Apresente a função is_short, que retorna true se e só se o valor recebido como argumento puder ser representado por um short sem perda de informação. Na implementação interna só podem ser utilizadas operações aritméticas e lógicas sobre inteiros. Qualquer operação de vírgula flutuante invalida o exercício.

bool is_short(float f);

desde já obrigado ;)

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

algoritmo para chegar à solução:

- obter o valor do expoente

- obter o valor da mantisa

- saber se aplicar o expoente à mantisa "resulta em algum valor extra"

mais do que isto é quase dar o código, isto porque isto é possivel fazer com uma única linha ou mesmo uma macro function


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

Bom problema!!! Também não estou a ver como fazer isso, sendo um comum mortal! :P...

Lembro-me de ter feito algo mas não sei se tinha a ver com isto. Era por exemplo decompor um valor da seguinte forma:

1254 = 1000 + 200 + 50 + 4

Mas isto se calhar não tem nada a ver! Já não me recordo! E pelo que tenho visto na net, o valor do sinal, expoente e mantissa são assuntos mais complexos que o que o HappyHippiHippo está a fazer parecer!

Mas tentando ir pela algoritmo do HappyHippiHippo

Considerando por exemplo o valor 453.039

O valor do expoente é -3

O valor da mantissa é 0.039 (acho eu)

Estou certo até aqui?

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

Considerando por exemplo o valor 453.039

O valor do expoente é -3

O valor da mantissa é 0.039 (acho eu)

Estou certo até aqui?

não.

como calcular o expoente:

453.039      / 2 = 226.5195
226.5195     / 2 = 113.25975
113.25975    / 2 = 56.629875
56.629875    / 2 = 28.3149375
28.3149375   / 2 = 14.15746875
14.15746875  / 2 = 7.078734375
7.078734375  / 2 = 3.5393671875
3.5393671875 / 2 = 1.76968359375

expoente = número de divisões + 127 = 8 + 127 = 135 = 10000111

como calcular a mantisa:

0.76968359375 * 2 = 1.5393671875
0.5393671875  * 2 = 1.078734375
0.078734375   * 2 = 0.15746875
0.15746875    * 2 = 0.3149375
0.3149375     * 2 = 0.629875
0.629875      * 2 = 1.25975
0.25975       * 2 = 0.5195
0.5195        * 2 = 1.039
0.039         * 2 = 0.078
0.078         * 2 = 0.156
0.156         * 2 = 0.312
0.312         * 2 = 0.624
0.624         * 2 = 1.248
0.248         * 2 = 0.496
0.496         * 2 = 0.992
0.992         * 2 = 1.984
0.984         * 2 = 1.968
0.968         * 2 = 1.936
0.936         * 2 = 1.872
0.872         * 2 = 1.744
0.744         * 2 = 1.488
0.488         * 2 = 0.976
0.976         * 2 = 1.952
0.952         * 2 = 1.904
0.904         * 2 = 1.808
...

mantisa = valores da parte inteira da sucessão de multiplicações = 11000101000010011111101 (o resto é descartado ... erro de percisão de um valor de virgula flutuante)

binário final:

01000011111000101000010011111101

agora, como resovler tudo em uma linha ? deixo isso para mais tarde :P (até porque a minha solução tem mais do que 350 caracteres, mas como é uma macro functions, não deixa de ser tudo uma linha)

Editado por HappyHippyHippo

IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

Então para o cálculo do expoente, temos que dividir por o valor em questão, 8 vezes, certo?

Para a mantissa não percebi...

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

para o expoente é necessário dividir o valor até ter algo do género : 1.XXXXXXX

o número de vezes que é necessário dividir dá o valor do expoente (somado do bias)

para a mantissa, é necessário pegar na parte fracionária do resultado anterior e multiplicar constantemente por 2, à parte fracionária do resultado anterior.

depois é pegar na parte inteira das multiplicações e combinar/montar o binário.


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

Então se quiser aproveitar os argumentos da consola, como é que usamos o argv[1] como um float??? Isto não é gravado em argv[] como uma string?

Ou seja, se eu fizer:

./float_num 453.039

o valor 453.039 fica em argv[1] mas como string, certo?

Como é que depois trabalhamos com esse valor como float?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

acho que o gman9 já teve tempo de pensar na resposta, ao qual eu só digo:

#define is_short(f)             ((((*(unsigned int*) &f) << 1) == 0) || (((((*(unsigned int*) &f) & 0x7fffff) << (9 + ((((*(unsigned int*) &f) >> 23) & 0xff) - 127))) == 0) && ((((((*(unsigned int*) &f) >> 23) & 0xff) - 127) < 15) || ((((*(unsigned int*) &f) >> 31) == 1) && (((((*(unsigned int*) &f) >> 23) & 0xff) - 127) == 15) && (((*(unsigned int*) &f) & 0x7fffff) == 0)))))

ps : sim, é tudo uma só instrução

Editado por HappyHippyHippo

IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

estás a dizer que para dar erro caso tenha texto a mais como por exemplo o "a32" no teu caso ? se sim

podes fazer uma marosca muito engraçada:

/*
char check;
float c;
*/
float f;
char c;
switch (sscanf(argv[1], "%f%c", &f, &c))
{
 case 1:
   // correct
   break;
 case 2:
   // have extra characters
   break;
 default: // zero
   // error : unable to read the float
   break;
}

Editado por HappyHippyHippo

IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

Pois, esses detalhes acabm por fazer a diferença... E não se se percebi bem como é que o sscanf funciona!

Mas por exemplo vamos analisar.

Neste caso específico, e segundo me apercebo, o sscanf lê o 52.0 para o "f" e deverá ler o "a" para o "c". Mas esta formatação que fizeste já não funciona caso o user input seja a25.036...

Ou seja, o meu objectivo era evitar qualquer engano por parte do user! (Isto acaba por sair fora do contexto do post, mas...)

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

Neste caso específico, e segundo me apercebo, o sscanf lê o 52.0 para o "f" e deverá ler o "a" para o "c". Mas esta formatação que fizeste já não funciona caso o user input seja a25.036...

funciona ... isso cai no default


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

O sscanf também apanha os '\n' que estão no buffer??? É que não estou a conseguir fazer funcionar essa marosca!

#include <stdio.h>
#include <stdlib.h>

#define CLEAR_SCREEN puts("\x1b[H\x1b[2J")
#define CLEAR_INPUT while (getchar () != '\n') /*void*/
#define INPUT_ERROR -1 
#define INPUT_OK     0

int main (int argc, char** argv){
   short int check = INPUT_ERROR;
   float num, c;

   if (argc < 2){
       printf ("Usage: ./floatnum <number>\n");
       exit (0);
   }

   while (check == INPUT_ERROR){
       switch (sscanf (argv[1], "%f%c", &num, &c)){
           case 1:  check = INPUT_OK;
                    break;
           case 2:  check = INPUT_ERROR;
                    printf ("That is not a number! Try again!\n");
                    CLEAR_INPUT;
                    break;
           default: check = INPUT_ERROR;
                    printf ("Unkown Error! Try again!");
                    break;
       }
   }

   printf ("Number is: %.2f\n", num);

   return 0;
}

o sscanf está sempre a retornar 2 mesmo que eu insira um número correcto!

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

não sei o que andas a testar, mas no código só tens o problema de que a variável c deveria ser um char


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

não sei o que andas a testar, mas no código só tens o problema de que a variável c deveria ser um char

Eu pus float porque tu to teu code também puseste o "c" como float!

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo    1151
HappyHippyHippo

Eu pus float porque tu to teu code também puseste o "c" como float!

epa .. o código foi feito de cabeça ... corrigido

Editado por HappyHippyHippo

IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

Ok...

Bom, testei o code mas ainda ssim há algo que não está a funcionar!

Se eu lançar o programa assim:

./floatnum 53.2a

ele detecta efectivamente que houve dois valores lidos. Mas se a seguir colocar um valor correcto, o programa continua a detectar 2 valores lidos!

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Flinger    50
Flinger

Debug$ ./tester 53.2
Number is: 53.20
Debug$ ./tester 53.2a
That is not a number! Try again!

Só se for o windows a fazer das suas :D

Editado por Flinger

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n    8
PsySc0rpi0n

Bom, parece que eu já estou a fazer asneira no code!!! Já estou a misturar argumentos enviados pelo terminal e dados pedidos pelo programa ao user!

Deixem-me cá refazer isto!

E não, deste lado não se usa Windows!

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.