¿Es 2 ** x más rápido de calcular que exp (x)?

12

Perdona la ingenuidad que será obvia en la forma en que hago esta pregunta, así como el hecho de que la estoy haciendo.

Los matemáticos suelen usar ya que es la base más simple / mejor en teoría (debido al cálculo). Pero las computadoras parecen hacer todo en binario, entonces, ¿es más rápido en una máquina para calcular que ?exp2**xMath::exp(x)

isomorfismos
fuente
77
¿De qué tipo de número estás hablando? Entero de tamaño arbitrario? Punto flotante de tamaño fijo? Punto flotante de precisión arbitraria?
Gilles 'SO- deja de ser malvado'
@Gilles Es un buen punto. No me di cuenta de que la diferencia era importante.
isomorphismes
3
He visto en algunas calculadoras de bolsillo Casio ese registro y la potencia de un número no-E es mucho más lento que ln / exp
phuclv
2
Para arriesgarse a ser franco, ¿ha intentado cronometrarlos y ver cuál es más rápido? ¿O estás hablando de velocidad en un sentido de complejidad ? O(f(n))
jmite
1
El idioma se encarga de seleccionar la forma más rápida y hará un buen trabajo. Solo en caso de que se requiera la máxima velocidad, y las mediciones han demostrado que esto es relevante para el rendimiento en caso de que se preocupe por este tipo de cosas
vonbrand

Respuestas:

18

Como se trata de CS y no de Stackoverflow, voy a suponer que está haciendo una pregunta sobre el análisis numérico y (para simplificar) el punto flotante IEEE-754 en particular. En ese caso, la respuesta a su pregunta depende en parte de lo que quiere decir con "más fácil", y en parte de los detalles del sistema.

No conozco ninguna CPU moderna que tenga una instrucción integrada que haga exactamente lo que esperaría, ya sea para la operación (que en adelante llamaremos , su nombre habitual en C) o ( ). Ambos se implementan utilizando funciones de biblioteca.exexp2xexp2

Como es el caso con todos los métodos numéricos para operaciones trascendentales, hay algunos casos especiales a considerar:

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

Sin embargo, hay otra cosa que hace que el problema sea un poco menos complicado: el dominio útil es bastante pequeño. Para binary32, se exp(x)desborda si o menos, y se desborda si o menos. Inusualmente para operaciones trascendentales, también podemos ignorar el caso subnormal, ya que es indistinguible de si es subnormal. Todo lo anterior también es cierto , excepto que el dominio es ligeramente diferente.x<104x>88.7exp(x)1.0xexp2

Su intuición es correcta en que la mayoría de las implementaciones calculan . Sin embargo, el costo de esa multiplicación por es trivial en comparación con el resto de la informática . Un método típico utiliza una tabla calculada previamente con elementos :ex=2x/ln21ln2exp2K

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

donde es la parte entera de , la tabla contiene valores de para todo en el rango , y es una aproximación polinómica a (cuarto es suficiente para binario32 ) en el rango . La parte es barata, ya que solo está manipulando el exponente. es una tabla de búsqueda. Por lo tanto, es probable que sea ​​la parte costosa de la operación.nxT2j/Kj[0,K)P2x2nTP[0,1K)2nTP

Debo señalar para completar que las FPU Intel x86 incluyen una instrucción llamada f2xm1, que calcula para en el rango . Sin embargo, en una CPU moderna, esta es una instrucción bastante costosa y no canalizada, y no se recomienda utilizarla. Como señala correctamente la Sección 3.8.5 del Manual de referencia de Intel Optimization :x [ - 1 , 1 ]2x1x[1,1]

Aunque x87 admite instrucciones trascendentales, la implementación de la biblioteca de software de la función trascendental puede ser más rápida en muchos casos.

Editar: Se ha señalado en los comentarios que debería explicar algo de la nueva terminología utilizada en IEEE 754-2008. Parte del lenguaje ha cambiado desde 1985 y 1987, y la mayoría de las personas están mucho más familiarizadas con la antigua jerga.

Los términos "binary32" y "binary64" son los nuevos nombres para los números de coma flotante binarios de 32 y 64 bits, que el estándar anterior llamaba "single" y "double" respectivamente.

El término "número subnormal" reemplaza el término anterior "número denormal" o "número desnormalizado" .

Seudónimo
fuente
cuando dices "subnormal" - claramente no quieres decir "subgaussiano"; ¿te refieres a "peor que [algún punto de referencia de la tipicidad]"?
isomorphismes
2
@isomorphismes Aquí, 'subnormal' es con respecto a cómo se implementan los flotadores. Ver números denormales en Wikipedia.
Paul Manta
Por cierto, simplifiqué un poco el "método típico". Es posible implementar exp2 () y exp () con precisión ulp usando solo una pequeña extensión (y bastante fácil de entender) del método presentado aquí, pero una explicación de la pequeña extensión fácil de entender probablemente duplicaría la longitud de ¡la respuesta!
Seudónimo
6

Si 2**xquiere decir , entonces sí. Podemos usar el operador de desplazamiento a la izquierda , es decir, calculamos . Esto es increíblemente rápido, ya que es una instrucción de máquina primitiva en todos los procesadores que conozco. Esto no se puede hacer con ninguna base que no sea 2. Además, la exponenciación de enteros siempre será más rápida que la exponenciación real, ya que los números de coma flotante tardan más en multiplicarse.2x<<1 << x

cabeza de jardín
fuente
44
no realmente. x tal vez un tipo de punto flotante
phuclv
1
Ah, perdón, supuse implícitamente que era un número entero. x
cabeza de jardín
Si xno es un número entero (digamos 20.75), establecería la mantisa 2y el exponente en el valor redondeado de xla estimación más precisa (no es posible la representación precisa). Lo cual también es mucho más rápido que `pow´.
Damon
1

Si 2**xes una función en enteros, entonces estoy de acuerdo con la respuesta de Stephen, shift es más barato. Pero típicamente veo eso como 2^xy **para indicar exponenciación de punto flotante. Para este caso, esperaría un rendimiento similar entre **y ^ya que ambos expy pow(la operación subyacente para **) son operaciones de aproximación trascendentales.

Tim
fuente
Interesante, no sabía que **se consideraba un sinónimo de la versión de punto flotante (y, tontamente, había olvidado que las dos serían diferentes).
isomorphismes
1

Dado que 2 ^ x = e ^ (x * ln 2) y e ^ x = 2 ^ (x * log2 (e)), no esperaría mucha diferencia.

Para x cercano a cero, generalmente se usaría un polinomio e ^ x = 1 + x + x ^ 2/2 + x ^ 3/6 ..., muy bien optimizado para cortar lo antes posible mientras se mantiene pequeño el error de redondeo . Claramente, 2 ^ x es un poco, un poquito más lento de calcular. "x cerca de 0" generalmente serían valores de x donde sqrt (1/2) <= e ^ x <= sqrt (2). Restringir el rango de x asegura que el grado polinómico no tenga que elegirse demasiado alto.

Para x más grande, generalmente se calcularía 2 ^ x dejando x = x '+ x' ', donde x' es un número entero y -0.5 <= x '' <= 0.5. Entonces 2 ^ x 'se calcularía construyendo un número de coma flotante con el patrón de bits correcto, y 2 ^ x' 'usando el método e ^ x para x pequeño. Aquí, 2 ^ x es un poquito más rápido. Además, si x es grande (digamos x = 100.3), solo multiplicar x por log2 (e) introduciría un error de redondeo inaceptable (porque hay muchos menos bits fraccionarios), por lo que se debe tener más cuidado.

Y con suerte, una buena función de biblioteca se encargaría de que siempre que x <= y, e ^ x <= e ^ y y 2 ^ x <= 2 ^ y, sin importar cuáles sean los errores de redondeo. Lograr ese tipo de cosas puede ser complicado.

gnasher729
fuente
0

Debe comprender que las matemáticas en una computadora se realizan de diferentes maneras por diferentes programas, con la esperanza de obtener respuestas consistentes. En cuanto a la mayoría del software, creo que las computadoras se comportan bien: las computadoras y calcularán la respuesta a la larga, incluso para 0 ^ 0. El problema es que los casos especiales involucran "reconocimiento" que no ocurre de forma gratuita en las computadoras digitales. Esto significa que solo en aquellos casos en que tener la respuesta acelerará las cosas "al máximo" se producirá la optimización. Pero en esos casos ocurrirá extremadamente bien. También tenga en cuenta que se deben hacer varios reconocimientos diferentes para obtener la respuesta correcta. Esto se llama niveles de optimización de velocidad y esto ha ocurrido en la máxima medida profesional en base a la mayoría del software llamado GNU "C". Esto se debe a que aquí se utilizan pequeñas diferencias en el tiempo de ejecución de software a software y de máquina a máquina como cifras de aceptación de calidad. En otros intérpretes, por lo general, solo si se produce un "indicador cero" como efecto secundario de los cálculos anteriores, se acelerará el reconocimiento. como 0 * x => C0.

marca
fuente