Estaba buscando un enfoque eficiente para calcular a b (digamos a = 2
y b = 50
). Para comenzar, decidí echar un vistazo a la implementación de la Math.Pow()
función. Pero en .NET Reflector , todo lo que encontré fue esto:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
¿Cuáles son algunos de los recursos en los que puedo ver lo que sucede dentro cuando llamo a la Math.Pow()
función?
InternalCall
con unextern
modificador (ya que parecen estar en conflicto), vea la pregunta (y las respuestas resultantes) que publiqué sobre esto mismo.2^x
operación six
es entera, el resultado es una operación de cambio. Entonces, tal vez podrías construir el resultado usando una mantisa de2
y un exponente dex
.Respuestas:
Eso significa que el método se implementa realmente en el CLR, escrito en C ++. El compilador justo a tiempo consulta una tabla con métodos implementados internamente y compila la llamada a la función C ++ directamente.
Echar un vistazo al código requiere el código fuente para el CLR. Puede obtener eso de la distribución SSCLI20 . Fue escrito alrededor del marco de tiempo de .NET 2.0, he encontrado que las implementaciones de bajo nivel
Math.Pow()
siguen siendo muy precisas para versiones posteriores de CLR.La tabla de búsqueda se encuentra en clr / src / vm / ecall.cpp. La sección relevante para se
Math.Pow()
ve así:La búsqueda de "COMDouble" lo lleva a clr / src / classlibnative / float / comfloat.cpp. Te ahorraré el código, solo échale un vistazo. Básicamente verifica si hay casos de esquina, luego llama a la versión de CRT
pow()
.El único otro detalle de implementación que es interesante es la macro FCIntrinsic en la tabla. Esa es una pista de que el jitter puede implementar la función como intrínseca. En otras palabras, sustituya la llamada a la función con una instrucción de código de máquina de coma flotante. Lo cual no es el caso
Pow()
, no hay instrucciones de FPU para ello. Pero ciertamente para las otras operaciones simples. Es notable que esto puede hacer que las matemáticas de coma flotante en C # sean sustancialmente más rápidas que el mismo código en C ++, verifique esta respuesta por la razón.Por cierto, el código fuente para el CRT también está disponible si tiene la versión completa del directorio Visual Studio vc / crt / src. Sin
pow()
embargo, te toparás con la pared , Microsoft compró ese código de Intel. Hacer un mejor trabajo que los ingenieros de Intel es poco probable. Aunque la identidad de mi libro de secundaria era el doble de rápido cuando lo probé:Pero no es un verdadero sustituto porque acumula el error de 3 operaciones de coma flotante y no se ocupa de los problemas de dominio raros que tiene Pow (). Como 0 ^ 0 y -Infinity elevado a cualquier poder.
fuente
pow
es notoriamente difícil de implementar con precisión, ya que es una función trascendental (ver el dilema del creador de tablas ). Es mucho más fácil con un poder integral.La respuesta de Hans Passant es excelente, pero me gustaría agregar que si
b
es un entero, entoncesa^b
se puede calcular de manera muy eficiente con la descomposición binaria. Aquí hay una versión modificada de Henry Warren's Hacker's Delight :Señala que esta operación es óptima (hace el número mínimo de operaciones aritméticas o lógicas) para todos b <15. Además, no existe una solución conocida para el problema general de encontrar una secuencia óptima de factores para calcular
a^b
para cualquier b que no sea un extenso buscar. Es un problema NP-Hard. Básicamente, eso significa que la descomposición binaria es tan buena como se pone.fuente
a
es un número de coma flotante.a
ser un número entero, pero el código sí. Como consecuencia de eso, me pregunto acerca de la precisión del resultado del cálculo "muy eficiente" del texto.Si la versión C de disponibilidad gratuita
pow
es una indicación, no se parece a nada de lo que cabría esperar. No sería de mucha ayuda para usted encontrar la versión .NET, porque el problema que está resolviendo (es decir, el que tiene números enteros) es un orden de magnitud más simple y puede resolverse en unas pocas líneas de código C # con la exponenciación mediante el algoritmo de cuadratura .fuente