Me gustaría formatear los siguientes números en los números junto a ellos con java:
1000 to 1k
5821 to 5.8k
10500 to 10k
101800 to 101k
2000000 to 2m
7800000 to 7.8m
92150000 to 92m
123200000 to 123m
El número de la derecha será largo o entero, el número de la izquierda será una cadena. ¿Cómo debería abordar esto? Ya hice un pequeño algoritmo para esto, pero pensé que podría haber algo inventado por ahí que haga un mejor trabajo y no requiera pruebas adicionales si empiezo a lidiar con miles de millones y billones :)
Requerimientos adicionales:
- El formato debe tener un máximo de 4 caracteres.
- Lo anterior significa que 1.1k está bien, 11.2k no lo está. Lo mismo para 7.8m está bien, 19.1m no lo está. Solo un dígito antes del punto decimal puede tener punto decimal. Dos dígitos antes del punto decimal significa no dígitos después del punto decimal.
- No es necesario redondear. (Los números que se muestran con k y m adjuntos son más de un indicador analógico que indica que la aproximación no es un artículo lógico preciso. Por lo tanto, el redondeo es irrelevante debido principalmente a la naturaleza de la variable que puede aumentar o decrementar varios dígitos incluso mientras mira el resultado en caché).
java
number-formatting
Mat B.
fuente
fuente
No rounding is necessary
esto? Me parece absurdo. ¿Es solo para complicar las cosas? ¿No sería mejor reformular estoRounding is not necessary, but welcome
?Respuestas:
Aquí hay una solución que funciona para cualquier valor largo y que encuentro bastante legible (la lógica central se realiza en las tres líneas inferiores del
format
método).Aprovecha
TreeMap
para encontrar el sufijo apropiado. Es sorprendentemente más eficiente que una solución anterior que escribí que usaba matrices y era más difícil de leer.Código de prueba
fuente
-5821
debe formatearse como-5k
, no como-5.8k
.-
para mantener el mismo número de dígitos significativos. Hay otras opciones ...Lo sé, esto se parece más a un programa en C, ¡pero es súper ligero!
Produce:
fuente
Aquí una solución que hace uso de la notación de ingeniería de DecimalFormat:
Salida:
fuente
Necesita alguna mejora, pero: StrictMath al rescate!
Puede poner el sufijo en una cadena o matriz y buscarlos en función de la potencia, o algo así.
La división también se puede administrar alrededor del poder, creo que casi todo se trata del valor del poder. ¡Espero eso ayude!
salidas:
fuente
Problemas con las respuestas actuales
Solución Java
Esta solución (una extensión de esta respuesta ) aborda los problemas anteriores.
Solución maravillosa
La solución se escribió originalmente en Groovy como se muestra a continuación.
Pruebas (Groovy)
Las pruebas se escriben en Groovy, pero se pueden usar para verificar la clase Java o Groovy (porque ambas tienen el mismo nombre y API).
fuente
La lib de la UCI tiene un formateador basado en reglas para los números, que puede usarse para deletrear números, etc. Creo que usar la UCI le daría una solución fácil de leer y mantener.
[Uso]
La clase correcta es RuleBasedNumberFormat. El formato en sí puede almacenarse como un archivo separado (o como String constant, IIRC).
Ejemplo de http://userguide.icu-project.org/formatparse/numbers
La misma página muestra números romanos, así que supongo que su caso también debería ser posible.
fuente
CompactDecimalFormat
. Nivel API 24+Con Java-12 + , puede usar
NumberFormat.getCompactNumberInstance
para formatear los números. Puedes crear unNumberFormat
primero comoy luego úsalo para
format
:fuente
Importante: Las respuestas a la conversión
double
fallarán para números como99999999999999999L
y regresar en100P
lugar de99P
porquedouble
usa elIEEE
estándar :Esta solución corta los dígitos no deseados y funciona para todos los
long
valores . Implementación simple pero eficiente (comparación a continuación). -120k no se puede expresar con 4 caracteres, incluso -0.1M es demasiado largo, es por eso que para números negativos 5 caracteres deben estar bien:La prueba
else if
al principio es necesaria porque el mínimo es-(2^63)
y el máximo es(2^63)-1
y, por lo tanto, la asignaciónnumber = -number
fallaría sinumber == Long.MIN_VALUE
. Si tenemos que hacer una verificación, entonces también podemos incluir tantos números como sea posible en lugar de simplemente verificarnumber == Long.MIN_VALUE
.La comparación de esta implementación con la que obtuvo la mayor cantidad de votos positivos (se dice que es la más rápida actualmente) mostró que es más de 5 veces más rápida (depende de la configuración de la prueba, pero con más números, la ganancia aumenta y esta implementación tiene hacer más comprobaciones porque maneja todos los casos, por lo que si el otro se solucionara, la diferencia sería aún mayor). Es así de rápido porque no hay operaciones de coma flotante, sin logaritmo, sin potencia, sin recursividad, sin expresiones regulares, sin formateadores sofisticados y minimización de la cantidad de objetos creados.
Aquí está el programa de prueba:
Salida posible:
2309 vs. 11591
(casi lo mismo cuando solo se usan números positivos y mucho más extremo al invertir el orden de ejecución, tal vez tenga algo que ver con la recolección de basura)fuente
Aquí hay una implementación corta sin recursión y solo un bucle muy pequeño. No funciona con números negativos, pero admite todos los
long
mensajes positivos hastaLong.MAX_VALUE
:Salidas:
También hice algunos puntos de referencia realmente simples (formateando 10 millones de largos aleatorios) y es considerablemente más rápido que la implementación de Elijah y un poco más rápido que la implementación de las asilias.
fuente
Para cualquiera que quiera redondear. Esta es una excelente solución fácil de leer que aprovecha la biblioteca Java.Lang.Math
fuente
El siguiente código muestra cómo puede hacer esto con una expansión fácil en mente.
La "magia" radica principalmente en la
makeDecimal
función que, para los valores correctos pasados, garantiza que nunca tendrá más de cuatro caracteres en la salida.Primero extrae las porciones enteras y décimas para un divisor dado, por ejemplo,
12,345,678
con un divisor de1,000,000
dará unwhole
valor de12
y untenths
valor de3
.A partir de eso, puede decidir si genera solo la parte completa o la parte entera y la décima, utilizando las reglas:
El código para eso sigue:
Entonces, es una simple cuestión de llamar a esa función auxiliar con los valores correctos, incluidas algunas constantes para facilitar la vida del desarrollador:
El hecho de que la
makeDecimal
función haga el trabajo pesado significa que expandirse más allá999,999,999
es solo cuestión de agregar una línea adicionalXlat
, tan fácil que lo he hecho por usted.La final
return
enXlat
no necesita un condicional ya que el valor más grande se puede mantener en un signo de 64 bits de largo es de sólo 9,2 trillones.Pero si, por algún requisito extraño, Oracle decide agregar un tipo de 128 bits
longer
o un tipo de 1024 bitsdamn_long
, estará listo para ello :-)Y, finalmente, un pequeño arnés de prueba que puede usar para validar la funcionalidad.
Puede ver en el resultado que le brinda lo que necesita:
fuente
No sé si es el mejor enfoque, pero esto es lo que hice.
--- Código ---
fuente
Mi función para convertir números grandes a números pequeños (con 2 dígitos). Puede cambiar el número de dígitos cambiando
#.##
enDecimalFormat
Pruebas
Espero que ayude
fuente
Mi Java está oxidado, pero así es como lo implementaría en C #:
Sería fácil ajustar esto para usar kilos CS (1,024) en lugar de kilos métricos, o agregar más unidades. Formatea 1,000 como "1.0 k" en lugar de "1 k", pero confío en que sea irrelevante.
Para cumplir con el requisito más específico "no más de cuatro caracteres", elimine los espacios antes de los sufijos y ajuste el bloque del medio de esta manera:
fuente
ToString
método no existe en Java; necesitaría un NumberFormat que pueda crear otros problemas (confidenciales, etc.).Mi favorito. También podría usar "k", etc., como indicador de decimal, como es común en el dominio electrónico. Esto le dará un dígito adicional sin espacio adicional.
La segunda columna intenta usar tantos dígitos como sea posible
Este es el codigo
fuente
Manteniéndome fiel a mi comentario de que valoraría la legibilidad por encima del rendimiento, aquí hay una versión en la que debe quedar claro lo que está sucediendo (suponiendo que haya utilizado
BigDecimal
s antes) sin comentarios excesivos (creo en el código de autodocumentación), sin preocuparse por el rendimiento (ya que no puedo imaginar un escenario en el que quieras hacer esto tantas millones de veces que el rendimiento incluso se vuelve una consideración).Esta versión:
BigDecimal
s para precisión y para evitar problemas de redondeoHALF_UP
como en las pruebasREQUIRED_PRECISION
)enum
para definir los umbrales, es decir, podría ajustarse fácilmente para usar KB / MB / GB / TB en lugar de k / m / b / t, etc., y por supuesto podría extenderse más alláTRILLION
si es necesarioThreshold.java :
NumberShortener.java :
(Descomente las
println
declaraciones o cambie para usar su registrador favorito para ver qué está haciendo).Y finalmente, las pruebas en NumberShortenerTest (JUnit 4 simple):
Siéntase libre de señalar en los comentarios si omití un caso de prueba significativo o si los valores esperados deberían ajustarse.
fuente
TreeMap
acercamiento. La "legibilidad" es subjetiva, por supuesto. ;-) Ahora, ¿qué pasa si alguien quiere redondear de manera diferente a truncar en su versión? (Por ejemplo, cuando se usa esto para indicar el tamaño del archivo, ¿quién querría truncar?) Si desea potencias de 2 en lugar de 10? Tendrías que reescribir un poco, ¿no? Como dije, deliberadamente no estaba tratando de jugar golf mi código, gran parte del cual podría haberse acortado (por ejemplo, nunca mantendría un if-then en una línea).Este es mi código. Limpio y sencillo.
fuente
Agregando mi propia respuesta, código Java, código autoexplicativo ...
fuente
Este fragmento de código es simplemente mortal, simple y limpio, y funciona totalmente:
fuente
prueba esto :
fuente
Salida:
fuente
fuente