Estou tentando encontrar uma expressão confiável e eficiente para calcular quantos dígitos decimais são necessários para escrever um número inteiro positivo.
Matematicamente, o número de dígitos decimais em um inteiro n
é 1 + floor(log(n))
, onde log é o logaritmo comum (base 10).
Existem várias maneiras de construir uma expressão equivalente usando funções internas, mas algumas delas fornecem resultados incorretos. Alguém pode explicar por quê?
Aqui está um exemplo.
Como calcular o log?
A maneira mais simples de calcular o logaritmo comum é usar a LOG10
função.
Se preferir uma função para todos os logaritmos, você pode usar a LOG
função e especificar a base 10 com o segundo parâmetro.
Antes de 2012, a função do SQL Server LOG
calculava apenas o log natural (base e=2,71828...). Você pode calcular o logaritmo para uma base arbitrária de um número dividindo o logaritmo natural do número pelo logaritmo natural da base.
A consulta a seguir calcula todas as três expressões para alguns valores de exemplo:
SELECT
Number,
LOG(Number, 10) AS LogAB,
LOG10(Number) AS LogTen,
LOG(Number) / LOG(10) AS LogOverLog
FROM (
VALUES (999), (1000), (1001)
) AS Tally (Number);
Resultado:
Number LogAB LogTen LogOverLog
----------- ---------------------- ---------------------- ----------------------
999 2.99956548822598 2.99956548822598 2.99956548822598
1000 3 3 3
1001 3.00043407747932 3.00043407747932 3.00043407747932
Escolhi os valores 999, 1000 e 1001 porque 1000 é um ponto onde o número de dígitos aumenta. 999 tem 3 dígitos, 1000 tem 4.
O valor das três expressões é visivelmente o mesmo e parece correto.
Vamos passar para o degrau do chão.
Como calcular o piso?
Você pode obter o piso de cada log no exemplo anterior usando uma consulta como esta:
SELECT
Number,
FLOOR(LOG(Number, 10)) AS FloorLogAB,
FLOOR(LOG10(Number)) AS FloorLogTen,
FLOOR(LOG(Number) / LOG(10)) AS FloorLogOverLog
FROM (
VALUES (999), (1000), (1001)
) AS Tally (Number);
Resultado:
Number FloorLogAB FloorLogTen FloorLogOverLog
----------- ---------------------- ---------------------- ----------------------
999 2 2 2
1000 2 3 2
1001 3 3 3
Os valores de cada expressão para 999 e 1001 são iguais e corretos. Se somarmos 1 a cada valor, teríamos uma contagem de 3 dígitos em 999 e uma contagem de 4 dígitos em 1001.
Os valores para 1000 não são os mesmos! Se adicionarmos 1 a cada valor, teremos uma contagem de 4 dígitos em 1000 se usarmos a LOG10
função e uma contagem de 3 dígitos se usarmos a LOG
função em qualquer uma das formas.
Há uma incoerência aqui!
Como FLOOR(3) pode ser igual a 2?
A implicação é clara: usar a LOG
função me daria uma contagem incorreta para alguns valores, então devo usar a LOG10
função.
Mas o valor de cada expressão de log em si é idêntico e correto. Por que a função floor produz valores diferentes de sua entrada?
devoluções
0x4008000000000000
é exatamente 3 .0x4007FFFFFFFFFFFF
é 2.99999999999999955591079014994 .Se você estiver procurando por uma expressão eficiente, talvez uma
CASE
expressão com os 10 casos diferentes realmente funcione com menos CPU do que calcular logaritmos (ou possivelmente você poderia ter expressões de caso aninhadas para fazer uma pesquisa trinária)Eu chamaria isso de problema de arredondamento...
Resultado
Exibindo o limite