É 2 ** x mais rápido de calcular que exp (x)?

12

Perdoe a ingenuidade que será óbvia na maneira como faço essa pergunta, bem como no fato de estar fazendo.

Os matemáticos normalmente usam , pois é a base mais simples / mais agradável da teoria (devido ao cálculo). Mas os computadores parecem fazer tudo em binário, então é mais rápido em uma máquina calcular do que ?exp2**xMath::exp(x)

isomorfismos
fonte
7
De que tipo de número você está falando? Inteiro do tamanho arbitrário? Ponto flutuante de tamanho fixo? Ponto flutuante de precisão arbitrária?
Gilles 'SO- stop being evil'
@ Gilles É um bom ponto. Não percebi que a diferença era importante.
Isomorphismes
3
Tenho visto em algumas calculadoras de bolso Casio que log e poder de um número não-e é muito mais lento do que ln / exp
phuclv
2
Para arriscar ser franco, você já tentou cronometrar os dois e ver qual é o mais rápido? Ou você está falando de velocidade no sentido de complexidade ? O(f(n))
jmite
1
O idioma é responsável por selecionar o caminho mais rápido e fará um bom trabalho. Apenas em caso de ser necessário a velocidade máxima, e medições mostraram que isso é relevante para o desempenho que você deve se preocupar com esse tipo de coisa
vonbrand

Respostas:

18

Como esse é o CS e não o Stackoverflow, vou assumir que você está fazendo uma pergunta sobre análise numérica e (para simplificar) o ponto flutuante IEEE-754 em particular. Nesse caso, a resposta à sua pergunta depende em parte do que você quer dizer com "mais fácil" e em parte nos detalhes do sistema.

Nenhuma CPU moderna que eu conheço possui uma instrução construída, que faz exatamente o que você esperaria para a operação (que daqui em diante chamaremos , seu nome usual em C) ou 2 x ( ). Ambos são implementados usando funções de biblioteca.exexp2xexp2

Como é o caso de todos os métodos numéricos para operações transcendentais, há alguns casos especiais a serem considerados:

exp(NaN) = NaN
exp(+Inf) = +Inf
exp(-Inf) = 0

No entanto, há outra coisa que torna o problema um pouco menos complicado: o domínio útil é bem pequeno. Para binary32, subfluxa exp(x)se ou mais e transborda se x > 88,7 ou menos. Invulgarmente para operações transcendentais, também podemos ignorar o caso subnormal, pois é indistinguível de se é subnormal. Todas as opções acima também são verdadeiras , exceto que o domínio é um pouco diferente.x<104x>88.7exp(x)1.0xexp2

Sua intuição está certa em que a maioria das implementações calcula . No entanto, o custo dessa multiplicação por 1ex=2x/ln2 é trivial comparado ao resto da computação. Um método típico usa uma tabela pré-computada comelementosK:1ln2exp2K

exp2(x)=2n×T[j]×P(y)

onde é a parte inteira de x , a tabela T contém valores de 2 j / K para todos os j no intervalo [ 0 , K ) e P é uma aproximação polinomial de 2 x (o quártico é suficiente para binário32) no intervalo [ 0 , 1nxT2j/Kj[0,K)P2x. Aparte2né barata, pois está apenas manipulando o expoente. Té uma tabela de pesquisa. Portanto,Pé provavelmente a parte mais cara da operação.[0,1K)2nTP

Devo salientar por completo que as FPUs Intel x86 incluem uma instrução chamada f2xm1, que calcula por x no intervalo [ - 1 , 1 ] . No entanto, em uma CPU moderna, essa é uma instrução bastante cara e sem pipeline, e você é altamente desencorajado de usá-la. Como observa corretamente a seção 3.8.5 do Intel Optimization Reference Manual :2x1x[1,1]

Embora o x87 suporte instruções transcendentais, a implementação da função transcendental na biblioteca de software pode ser mais rápida em muitos casos.

Edit: Foi indicado nos comentários que devo explicar algumas das novas terminologias usadas no IEEE 754-2008. Parte da linguagem mudou desde 1985 e 1987, e a maioria das pessoas está muito mais familiarizada com o velho jargão.

Os termos "binary32" e "binary64" são os novos nomes para números de ponto flutuante binário de 32 e 64 bits, que o antigo padrão chamou de "único" e "duplo", respectivamente.

O termo "número subnormal" substitui o termo anterior "número desnormal" ou "número desnormalizado" .

Pseudônimo
fonte
quando você diz "subnormal" - você claramente não quer dizer "sub-gaussiano"; você quer dizer "pior do que [alguma referência de tipicidade]"?
Isomorfismes
2
@ isomorphismes Aqui, 'subnormal' é com relação a como os carros alegóricos são implementados. Veja números desnormais na Wikipedia.
Paul Manta
Aliás, simplifiquei um pouco o "método típico". É possível implementar exp2 () e exp () com precisão de ulp usando apenas uma pequena extensão (e bastante fácil de entender) do método apresentado aqui, mas uma explicação da pequena extensão fácil de entender provavelmente dobraria o comprimento de a resposta!
Pseudônimo
6

2**x2x<<1 << x

Gardenhead
fonte
4
na verdade não. x talvez um tipo de ponto flutuante
phuclv
1
x
Se xnão for um número inteiro (digamos 20.75), você definiria a mantissa como 2e o expoente como o valor arredondado xcomo a estimativa mais precisa (representação precisa não sendo possível). O que também é muito mais rápido que o pow.
Damon
1

Se 2**xfor uma função em números inteiros, concordo com a resposta de Stephen, o deslocamento é mais barato. Mas normalmente vejo isso como 2^xe **para indicar exponenciação de ponto flutuante. Para este caso, eu esperaria um desempenho semelhante entre **e ^uma vez que ambos expe pow(a operação subjacente para **) são operações de aproximação transcendental.

Tim
fonte
Interessante, eu não sabia que **era considerado sinônimo da versão de ponto flutuante (e, bobo, eu tinha esquecido que as duas seriam diferentes).
Isomorphismes
1

Como 2 ^ x = e ^ (x * ln 2) e e ^ x = 2 ^ (x * log2 (e)), você não esperaria muita diferença.

Para x próximo a zero, usualmente se usaria um polinômio e ^ x = 1 + x + x ^ 2/2 + x ^ 3/6 ..., bem otimizado para cortar o mais rápido possível, mantendo pequeno o erro de arredondamento . Claramente 2 ^ x é um pouquinho, um pouquinho mais lento para calcular. "x próximo a 0" normalmente seria valores de x em que sqrt (1/2) <= e ^ x <= sqrt (2). A restrição do intervalo de x garante que o grau polinomial não precise ser escolhido muito alto.

Para x maior, usualmente calcularíamos 2 ^ x deixando x = x '+ x' ', onde x' é um número inteiro e -0,5 <= x '' <= 0,5. 2 ^ x 'seria então calculado construindo um número de ponto flutuante com o padrão de bits correto e 2 ^ x' 'usando o método e ^ x para x pequeno. Aqui, 2 ^ x é um pouco mais rápido. Além disso, se x é muito amplo (digamos x = 100,3), apenas multiplicar x por log2 (e) introduziria um erro de arredondamento inaceitável (porque há muito menos bits fracionários), portanto, é necessário mais cuidado.

E, esperançosamente, uma boa função de biblioteca cuidaria para que sempre que x <= y, e ^ x <= e ^ y e 2 ^ x <= 2 ^ y, não importassem quais fossem os erros de arredondamento. Conseguir esse tipo de coisa pode ser complicado.

gnasher729
fonte
0

Você precisa entender que a matemática em um computador é feita de maneiras diferentes por diferentes softwares, com esperança de obter respostas consistentes. Olhando para a maioria dos softwares, acho que os computadores se comportam como bem - computadores e calcularão a resposta até mesmo para 0 ^ 0. O problema é que casos especiais envolvem "reconhecimento", o que não ocorre de graça em computadores digitais. Isso significa que somente nos casos em que ter a resposta acelerará as coisas "o máximo" ocorrerá a otimização. Mas, nesses casos, ocorrerá extremamente bem. Observe também que vários reconhecimentos diferentes podem ter que ser feitos para obter a resposta certa. Isso é chamado de níveis de otimização de velocidade e isso ocorreu na extensão profissional máxima na base da maioria dos softwares chamados GNU "C". Isso ocorre porque aqui são usadas pequenas diferenças no tempo de execução de software para software e de máquina para máquina como valores de aceitação da qualidade. Em outros intérpretes, normalmente, apenas se um "sinalizador zero" ocorrer como efeito colateral dos cálculos anteriores acelerará o reconhecimento. como 0 * x => C0.

Marca
fonte