Formate um número de ponto flutuante exatamente como decimal

9

Qualquer ponto flutuante binário pode ser formatado exatamente em decimal. A sequência resultante pode ser um pouco longa, mas é possível. No meu artigo sobre ponto flutuante , abordo a importância da precisão e agora quero essa função. Esse desafio é escrever um programa ou função que use um valor de ponto flutuante como entrada e formate uma string decimal exata como saída.

Para garantir que estamos trabalhando com os números de ponto flutuante corretos, um formato preciso deve ser fornecido como entrada para o programa. Este formato será dois inteiros Significand Exponent, onde o valor real do ponto flutuante é Significand * 2 ^ Exponent. Observe que qualquer valor pode ser negativo.

Específicos:

  • O alcance e a precisão de pelo menos um flutuador de 32 bits devem ser suportados (nenhuma entrada vai além disso)
  • O valor decimal formatado deve ser uma representação exata (basta fechar o suficiente para garantir que uma ponta redonda correta volte a flutuar não é o suficiente)
  • Não confiamos que as funções de formatação de ponto flutuante da biblioteca padrão sejam corretas o suficiente nem rápidas o suficiente (ex:) printfe, portanto, elas não podem ser usadas. Você deve fazer a formatação. Funções de formatação / conversão integrais são permitidas.
  • Pode não haver zeros à esquerda ou à direita, exceto o zero inicial necessário na frente do .se não houver um componente de número inteiro
  • É permitida uma função ou programa inteiro.

Exemplos:

1 -2 => 0.25
17 -3 => 2.125
-123 11 => -251904
17 50 => 19140298416324608
23 -13 => 0.0028076171875
3 120 => 3987683987354747618711421180841033728
3 -50 => 0.00000000000000266453525910037569701671600341796875
-3 -50 => -0.00000000000000266453525910037569701671600341796875
10 -2 => 2.5
-12345 -3 => -1543.125
0 0 => 0
161 -4 => 10.0625
512 -3 => 64

O menor código vence.

edA-qa mort-ora-y
fonte
3
É permitido o uso de aritmética de ponto flutuante de precisão ilimitada?
Dennis
2
Se o expoente não for negativo, podemos terminar com .0?
Sp3000
@Dennis: Sim, aritmética de precisão fixa ilimitada ou alta é permitida.
edA-qa mort-ora-y
11
Eu acho isso inconsistente. Se 0.abcnão for um zero inicial, não será um zero abc.0à direita.
orlp
11
Também é convenção sempre terminar com .0números inteiros ao lidar com números de ponto flutuante. Veja, por exemplo, Python: str(1.0) == '1.0'versus str(1) == '1'. Sua lógica ainda é inconsistente.
orlp

Respostas:

3

CJam, 43

r_'-&\ize999rim<s1e3'0e[W%999/(i_L?\+'.*sW%

Experimente online

Explicação:

O programa trabalha com expoentes de até ± 999, quase com precisão dupla (64 bits). Ele separa o sinal de menos (se presente) do significando, multiplica por 10 999 e depois muda um pouco com o expoente, que agora é um cálculo exato. Em seguida, ele fecha à esquerda com zeros se o resultado tiver menos de 1000 dígitos, separa os últimos 999 dígitos como parte fracionária, remove os zeros à direita convertendo seu reverso para inteiro, adiciona um ponto decimal, se necessário, e junta tudo novamente.

r_         read and duplicate the significand in string form
'-&        keep only the minus sign, if present
\          swap with the other copy of the significand
iz         convert to integer and get absolute value
e999       multiply by 10^999
ri         read the exponent and convert to integer
m<         shift left by it; negative values will shift right
            the result is an exact non-negative integer
s          convert to string
1e3'0e[    pad to the left with zero characters up to length 1000
            longer strings will be left intact
            we need 1 more than 999 for the 0.xxx case
W%         reverse the string
999/       split into slices of length 999
(          take out the first slice (reversed fractional part)
i          convert to integer
            this removes the leading zeros (trailing in reverse)
_L?        if it's zero, replace with an empty string
\+         concatenate back (to the left) with the second slice
'.*        join the with the dot character
            if the fractional part was zero, we only have the second slice
            (reversed integer part) and there is nothing to join
s          convert to string; this is the reversed result without the sign
W%         reverse back

No final, o sinal de menos (se houver) e a sequência final são automaticamente impressos juntos.

aditsu sair porque SE é MAU
fonte
2

CJam, 50 bytes

q~A1$z#\_0>K5?\z:E#@_s'-&oz*\md_sE'0e[W%isW%'.\+Q?

Este é um programa completo que lê de STDIN. Experimente on-line no intérprete CJam .

Verifique todos os casos de teste de uma só vez.

Dennis
fonte
Baseado no seu comentário, presumo que o CJam tenha precisão ilimitada e você o usou aqui? É correto, então, que esta resposta cubra qualquer entrada, não apenas a flutuação de 32 bits? Além disso, podemos obter uma explicação de como isso funciona?
edA-qa mort-ora-y
O CJam possui precisão ilimitada para números inteiros, mas apenas flutuações de precisão dupla. Multiplico por uma potência de 20 para expoentes positivos e uma potência de 5 para negativos, o elenco para amarrar e inserir o ponto. Vou adicionar uma explicação detalhada em algumas horas.
Dennis
E sim, com memória suficiente, isso deve funcionar para qualquer entrada.
Dennis
10 -2 parece ter um zero à direita
aditsu encerrou porque SE é EV
@aditsu: Ah sim, um zero à direita para cada potência de 2 ...
Dennis
2

GNU sed + dc, 65

A pontuação inclui +1 para a -ropção do sed .

y/-/_/
s/.*/dc -e"C8k& 2r^*p"/e
s/\\\n//
s/0+$//
s/^(-?)\./\10./

Fiquei tentado a reivindicar esta dcresposta apenas C8k& 2r^*ppara uma pontuação de 10, mas dctem algumas peculiaridades de formatação:

  • o sinal -ve é em _vez de-
  • linhas longas são quebradas com barras invertidas
  • zeros à direita devem ser removidos
  • 0 inicial para |n| < 1deve ser adicionado

Portanto, a expressão dc é agrupada e avaliada sedpara cuidar do acima.

Saída de teste:

$ echo "1 -2
17 -3
-123 11
17 50
23 -13
3 120
3 -50
-3 -50
8388608 127
1 -127" | sed -rf float.sed
0.25
2.125
-251904
19140298416324608
0.0028076171875
3987683987354747618711421180841033728
0.00000000000000266453525910037569701671600341796875
-0.00000000000000266453525910037569701671600341796875
1427247692705959881058285969449495136382746624
0.0000000000000000000000000000000000000058774717541114375398436826861112283890933277838604376075437585313920862972736358642578125
$ 
Trauma Digital
fonte
Hmm, acho que isso dcviola minha regra de usar uma função de formatação padrão.
edA-qa mort-ora-y
11
@ edA-qamort-ora-y Imaginei que o uso de dcestá ok, dado que "aritmética de precisão fixa ilimitada ou de alta precisão é permitida" . dcO pcomando não é uma " função de formatação de ponto flutuante" - é uma função de impressão de precisão arbitrária. Estou definindo a precisão para 128 casas decimais ( C8k), o que acho mais do que suficiente para qualquer flutuação de 32 bits.
Digital Trauma