Formatee un número de coma flotante exactamente como decimal

9

Cualquier punto flotante binario puede formatearse exactamente en decimal. La cadena resultante puede ser algo larga, pero es posible. En mi artículo sobre coma flotante , cubro la importancia de la precisión, y ahora quiero esta función. Este desafío es escribir un programa o función que tome un valor de coma flotante como entrada y formatee una cadena decimal exacta como salida.

Para garantizar que estamos trabajando con los números de coma flotante correctos, se debe proporcionar un formato preciso como entrada al programa. Este formato será dos enteros Significand Exponent, donde está el valor real de coma flotante Significand * 2 ^ Exponent. Tenga en cuenta que cualquier valor puede ser negativo.

Detalles específicos:

  • El rango y la precisión de al menos un flotante de 32 bits deben ser compatibles (ninguna entrada irá más allá de eso)
  • El valor con formato decimal debe ser una representación exacta (simplemente lo suficientemente cerca como para garantizar una vuelta correcta de punta redonda al flotador no es lo suficientemente bueno)
  • No confiamos en que las funciones de formato de punto flotante de la biblioteca estándar sean lo suficientemente correctas ni lo suficientemente rápidas (por ejemplo:) printf, y por lo tanto no se pueden usar. Debes hacer el formateo. Se permiten funciones integrales de formato / conversión.
  • Puede que no haya ceros iniciales o finales, excepto el cero inicial requerido delante de .si no hay un componente de número entero
  • Se permite una función o un programa completo.

Ejemplos:

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

El código más corto gana.

edA-qa mort-ora-y
fuente
3
¿Se permite el uso de aritmética de coma flotante de precisión ilimitada?
Dennis
2
Si el exponente no es negativo, ¿podemos terminar con .0?
Sp3000
@Dennis: Sí, se permite la aritmética de precisión fija ilimitada o alta.
edA-qa mort-ora-y
1
Creo que eso es inconsistente. Si 0.abcno es un cero inicial, entonces abc.0no es uno posterior.
orlp
1
También es convencional terminar siempre con .0números enteros cuando se trata de números de coma flotante. Ver por ejemplo Python: str(1.0) == '1.0'versus str(1) == '1'. Tu lógica sigue siendo inconsistente.
orlp

Respuestas:

3

CJam, 43

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

Pruébalo en línea

Explicación:

El programa funciona con exponentes de hasta ± 999, cerca de la precisión doble (64 bits). Separa el signo menos (si está presente) del significado, lo multiplica por 10 999 y luego cambia un poco con el exponente, que ahora es un cálculo exacto. Luego se rellena a la izquierda con ceros si el resultado tiene menos de 1000 dígitos, separa los últimos 999 dígitos como la parte fraccionaria, elimina los ceros finales al convertir su reverso en entero, agrega un punto decimal si es necesario y vuelve a unir todo.

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

Al final, el signo menos (si lo hay) y la cadena final se imprimen automáticamente juntos.

aditsu renunció porque SE es MALO
fuente
2

CJam, 50 bytes

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

Este es un programa completo que lee desde STDIN. Pruébelo en línea en el intérprete de CJam .

Verifique todos los casos de prueba a la vez.

Dennis
fuente
Según su comentario, supongo que CJam tiene una precisión ilimitada y ¿lo ha usado aquí? ¿Es correcto entonces que esta respuesta cubra cualquier entrada, no solo flotante de 32 bits? Además, ¿podemos obtener una explicación de cómo funciona?
edA-qa mort-ora-y
CJam tiene precisión ilimitada para enteros, pero solo flota flotante de doble precisión. Multiplico por una potencia de 20 para los exponentes positivos y una potencia de 5 para los negativos, el elenco a cadena e inserta el punto. Agregaré una explicación detallada en unas pocas horas.
Dennis
Y sí, con suficiente memoria, esto debería funcionar para cualquier entrada.
Dennis
10-2
@aditsu: Ah sí, un cero al final por cada potencia de 2 ...
Dennis
2

GNU sed + dc, 65

La puntuación incluye +1 para la -ropción de sed .

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

Tuve la tentación de reclamar esta dcúnica respuesta C8k& 2r^*ppor un puntaje de 10, pero dctiene algunas peculiaridades de formato:

  • el signo -ve es en _lugar de-
  • las líneas largas se rompen con barras invertidas
  • los ceros finales deben eliminarse
  • |n| < 1debe agregarse 0 para

Entonces, la expresión dc se envuelve y se evade sedpara encargarse de lo anterior.

Prueba de salida:

$ 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
fuente
Hmm, creo que dceso viola mi regla sobre el uso de una función de formato estándar.
edA-qa mort-ora-y
1
@ edA-qamort-ora-y Pensé que el uso de dcestá bien, dado que "se permite la aritmética de precisión fija ilimitada o alta" . dcEl pcomando no es una " función de formato de punto flotante" , es una función de impresión de precisión arbitraria. Estoy configurando la precisión a 128 decimales ( C8k), lo que creo que es más que suficiente para cualquier flotante de 32 bits.
Trauma digital