Eu gostaria de formatar os seguintes números nos números próximos a eles com 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
O número à direita será longo ou inteiro, o número à esquerda será string. Como devo abordar isso. Eu já fiz pouco algoritmo para isso, mas pensei que talvez já houvesse algo inventado por aí que faça um trabalho melhor e não exija testes adicionais se eu começar a lidar com bilhões e trilhões :)
Requisitos adicionais:
- O formato deve ter no máximo 4 caracteres
- O acima significa 1,1k é OK 11,2k não é. Mesmo para 7,8m está OK 19,1m não é. Apenas um dígito antes do ponto decimal pode ter ponto decimal. Dois dígitos antes do ponto decimal não significa dígitos após o ponto decimal.
- Não é necessário arredondar. (Os números exibidos com k e m anexados são mais medidores analógicos, indicando um artigo lógico não aproximado. Portanto, o arredondamento é irrelevante principalmente devido à natureza da variável que pode aumentar ou decretar vários dígitos, mesmo enquanto você está observando o resultado em cache.)
java
number-formatting
Mat B.
fonte
fonte
No rounding is necessary
disso parece absurdo para mim. É apenas para complicar as coisas? Não seria melhor reformular issoRounding is not necessary, but welcome
?Respostas:
Aqui está uma solução que funciona com qualquer valor longo e que eu acho bastante legível (a lógica principal é feita nas três linhas inferiores do
format
método).Ele utiliza
TreeMap
para encontrar o sufixo apropriado. É surpreendentemente mais eficiente do que uma solução anterior que escrevi que usava matrizes e era mais difícil de ler.Código de teste
fonte
-5821
deve ser formatado como-5k
, não como-5.8k
.-
para manter o mesmo número de dígitos significativos. Existem outras opções ...Eu sei, isso parece mais um programa C, mas é super leve!
Emite:
fonte
Aqui está uma solução que utiliza a notação de engenharia da DecimalFormat:
Resultado:
fonte
Precisa de algumas melhorias, mas: StrictMath para o resgate!
Você pode colocar o sufixo em uma String ou matriz e buscá-los com base no poder, ou algo assim.
A divisão também pode ser gerenciada em torno do poder, acho que quase tudo é sobre o valor do poder. Espero que ajude!
saídas:
fonte
Problemas com as respostas atuais
Solução Java
Esta solução (uma extensão desta resposta ) soluciona os problemas acima.
Solução Groovy
A solução foi originalmente escrita em Groovy, como mostrado abaixo.
Testes (Groovy)
Os testes são gravados no Groovy, mas podem ser usados para verificar a classe Java ou Groovy (porque ambos têm o mesmo nome e API).
fonte
A biblioteca ICU possui um formatador baseado em regras para números, que pode ser usado para digitar números etc. Acho que usar a ICU daria uma solução legível e sustentável.
[Uso]
A classe certa é RuleBasedNumberFormat. O formato em si pode ser armazenado como arquivo separado (ou como String constante, IIRC).
Exemplo de http://userguide.icu-project.org/formatparse/numbers
A mesma página mostra números romanos, então acho que seu caso também deve ser possível.
fonte
CompactDecimalFormat
. API Nível 24+Com o Java-12 + , você pode usar
NumberFormat.getCompactNumberInstance
para formatar os números. Você pode criar umNumberFormat
primeiro comoe use-o para
format
:fonte
Importante: A conversão de respostas para
double
falhará para números como99999999999999999L
e retornará em100P
vez de99P
porquedouble
usa oIEEE
padrão :Esta solução corta dígitos indesejados e funciona para todos os
long
valores . Implementação simples, mas com bom desempenho (comparação abaixo). -120k não pode ser expresso com 4 caracteres, até -0,1M é muito longo, por isso, para números negativos, 5 caracteres devem estar bem:O teste no
else if
início é necessário porque o mínimo é-(2^63)
e o máximo é(2^63)-1
e, portanto, a atribuiçãonumber = -number
falharia senumber == Long.MIN_VALUE
. Se precisarmos fazer uma verificação, podemos incluir o maior número possível de números, em vez de apenas procurarnumber == Long.MIN_VALUE
.A comparação desta implementação com a que obteve o maior número de votos (atualmente considerado o mais rápido) mostrou que é mais de 5 vezes mais rápido (depende das configurações do teste, mas com mais números o ganho fica maior e esta implementação tem para fazer mais verificações porque lida com todos os casos; portanto, se o outro for corrigido, a diferença se tornará ainda maior). É tão rápido porque não há operações de ponto flutuante, logaritmo, potência, recursão, regex, formatadores sofisticados e minimização da quantidade de objetos criados.
Aqui está o programa de teste:
Saída possível:
2309 vs. 11591
(aproximadamente o mesmo quando se usa apenas números positivos e muito mais extremo ao reverter a ordem de execução, talvez isso tenha algo a ver com a coleta de lixo)fonte
Aqui está uma pequena implementação sem recursão e apenas um loop muito pequeno. Não funciona com números negativos, mas suporta todos os
long
s positivos atéLong.MAX_VALUE
:Saídas:
Também fiz alguns testes simples (formatação de 10 milhões de longitudes aleatórias) e é consideravelmente mais rápido que a implementação de Elijah e um pouco mais rápido que a implementação de assylias.
fonte
Para quem quer arredondar. Esta é uma solução ótima e fácil de ler, que tira proveito da biblioteca Java.Lang.Math
fonte
O código a seguir mostra como você pode fazer isso com a expansão fácil em mente.
A "mágica" está principalmente na
makeDecimal
função que, pelos valores corretos transmitidos, garante que você nunca terá mais de quatro caracteres na saída.Primeiro, extrai a parte inteira e a décima parte de um determinado divisor, de modo que, por exemplo,
12,345,678
com um divisor de1,000,000
, forneça umwhole
valor de12
e umtenths
valor de3
.A partir disso, ele pode decidir se produz apenas a parte inteira ou a parte inteira e a décima parte, usando as regras:
O código para o seguinte:
Em seguida, basta chamar essa função auxiliar com os valores corretos, incluindo algumas constantes para facilitar a vida do desenvolvedor:
O fato de a
makeDecimal
função fazer o trabalho pesado significa que expandir além999,999,999
é apenas uma questão de adicionar uma linha extra aXlat
, tão fácil que eu fiz para você.A final
return
emXlat
não precisa de uma condicional desde o maior valor que você pode segurar em um 64-bit assinado longa é de apenas cerca de 9,2 quintilhões.Mas se, por algum requisito bizarro, a Oracle decidir adicionar um
longer
tipo de 128 bits ou um de 1024 bitsdamn_long
, você estará pronto para isso :-)E, finalmente, um pouco de equipamento de teste que você pode usar para validar a funcionalidade.
Você pode ver na saída que ela fornece o que você precisa:
fonte
Não sei se é a melhor abordagem, mas foi o que fiz.
--- Código ---
fonte
Minha função para converter número grande em número pequeno (com 2 dígitos). Você pode alterar o número de dígitos pela mudança
#.##
naDecimalFormat
Teste
Espero que ajude
fonte
Meu Java está enferrujado, mas aqui está como eu o implementaria em C #:
Seria fácil ajustar isso para usar quilos de CS (1.024) em vez de quilos métricos ou para adicionar mais unidades. Ele formata 1.000 como "1,0 k" em vez de "1 k", mas acredito que isso é irrelevante.
Para atender ao requisito mais específico "não mais que quatro caracteres", remova os espaços antes dos sufixos e ajuste o bloco do meio da seguinte maneira:
fonte
ToString
método não existe em Java - você precisaria de um NumberFormat que possa criar outros problemas (sensíveis ao código do idioma etc.).Meu favorito. Você também pode usar "k" e assim por diante como indicador para decimal, como é comum no domínio eletrônico. Isso lhe dará um dígito extra sem espaço adicional
A segunda coluna tenta usar o máximo de dígitos possível
Este é o código
fonte
Mantendo-me fiel ao meu comentário de que eu valorizaria a legibilidade acima do desempenho, aqui está uma versão em que deve ficar claro o que está acontecendo (supondo que você tenha usado
BigDecimal
s antes) sem comentar excessivamente (acredito em código de auto-documentação), sem se preocupar com o desempenho (como não consigo imaginar um cenário em que você deseje fazer isso tantas milhões de vezes que o desempenho se torna uma consideração).Esta versão:
BigDecimal
s para precisão e para evitar problemas de arredondamentoHALF_UP
como nos testesREQUIRED_PRECISION
)enum
para definir os limites, ou seja, pode ser facilmente ajustado para usar KB / MB / GB / TB em vez de k / m / b / t, etc. e pode, obviamente, ser estendido além,TRILLION
se necessárioThreshold.java :
NumberShortener.java :
(Remova o comentário das
println
instruções ou altere para usar seu criador de logs favorito para ver o que está fazendo.)E, finalmente, os testes no NumberShortenerTest (simples JUnit 4):
Sinta-se à vontade para apontar nos comentários se perdi um caso de teste significativo ou se os valores esperados devem ser ajustados.
fonte
TreeMap
abordagem. "Legibilidade" é subjetivo, é claro. ;-) Agora, e se alguém quiser arredondar de forma diferente do que truncar na sua versão? (Por exemplo, ao usar isso para indicar o tamanho do arquivo, quem deseja truncar?) Se você deseja potências de 2 em vez de 10? Você teria que reescrever um pouco, não é? Como eu disse, eu não estava deliberadamente tentando jogar meu código, muito do que poderia ter sido reduzido (eu nunca manteria um if-then em uma linha, por exemplo).esse é o meu código limpo e simples.
fonte
Adicionando minha própria resposta, código Java, código auto-explicativo ..
fonte
Este trecho de código é simplesmente mortal, simples e limpo, e funciona totalmente:
fonte
tente isto:
fonte
Resultado:
fonte
fonte