El trastero de José Juan Valid XHTML 1.1 Valid CSS! Estilo de página alternativo
Artículo creado en 2008.
Valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración sobre 3 comentarios.

Sobrecarga de operadores

La sobrecarga de operadores es una poderosa herramienta de programación. Con ella podemos por ejemplo, obtener estadísticas de todas las operaciones que se realizan en un pograma de forma muy sencilla, de manera que conozcamos que coste en sumas, multiplicaciones, divisiones, etc... tienen determinados algoritmos en la práctica (no en todos los algoritmos es sencillo calcular analíticamente su coste concreto en operaciones por lo que se deben usar análisis empíricos).

Vamos a implementar (en C#) una clase que permita realizar y controlar todas las operaciones que habitualmente se realizan sobre números en coma flotante, y ésto, sin que debamos realizar ningún tipo de esfuerzo mientras implementamos los algoritmos sobre los que usamos este tipo de números.

Definición del tipo a usar

Una de las cosas que se suelen hacer cuando se programa en C++ es abstraer el tipo de representación de coma flotante que se va a usar para los cálculos numéricos. Cada representación (principalmente double o float aunque pueden ser otros) tiene sus ventajas y sus inconvenientes, esto habitualmente se hace de tres formas:

En C++, cualquiera de las dos últimas soluciones permitirían definir mediante sobrecarga de operadores lo que pretendemos (pues en la sobrecarga de operadores es independiente de que se usen clases o no), no obstante la más adecuada como veremos es la última.

Vale, ¿y en C#?

En C# sólo podemos utilizar el último método, pues ninguno de los dos anteriores están disponibles, sin embargo, para nuestros propósitos no pasa nada, pues igualmente pensábamos definir una clase que encapsule toda nuestra sobrecarga.

¿Qué vamos a hacer exactamente?

Vamos a crear un nuevo tipo que encapsule todas las operaciones usuales que se realizan sobre los reales permitíendonos modificar fácilmente el tipo de real a usar en diferentes implementaciones y registrar estadísticas de las operaciones que se realizan sin que debamos modificar ni realizar tareas adicionales en las aplicaciones en las que usemos este tipo.

El registrar estadísticas de todas las operaciones (matemáticas) que realiza un programa conlleva un coste adicional (el que supone realizar ese registro), lógicamente podremos configurar la clase para que las registre o no y, en tal caso, no afecte en absoluto a las versiones release.

Acerca de la implementación

Si estás interesado en conocer los detalles de las herramientas de programación que se usan en esta implementación te sugiero amplíes en otras fuentes (por ejemplo en MSDN) pues sólo las expondré superficialmente.

Creación de la clase

Puesto que pretendemos realizar una representación de los números reales, llamaremos a la clase Real y como no podemos heredar (es realmente una pena) del tipo básico double (ni float) debemos establecer un miembro (privado, por supuesto) que almacene el valor en cuestión. Este es el aspecto que tomará nuestra clase.

Aspecto general de la clase Real

Registro de estadísticas

Las estadísticas deberán basarse lógicamente en las operaciones que se han realizado y como lo que más nos va a importar es el número de operaciones realizadas de cada tipo (podría ser interesante también la distribución de éstas en el tiempo, el código, según los datos, etc... pero esto es algo mucho más ambicioso) tendremos que mantener un contador para cada una de ellas. Además, estos contadores deben ser globales a toda la aplicación por tanto dichos miembros deberán ser estáticos.

Miembros para registrar las estadísticas.

Como querremos que se pueda acceder a estos datos desde fuera de la clase, sin modificarlos, estableceremos dichos valores como propiedades de sólo lectura.

Miembros para publicar las estadísticas.

Algo bastante habitual será que nos interese un contador general que aglutine una familia de operaciones, obviamente ese contador no existe (se calcula como la suma de otros), pero será interesante tener una propiedad (de sólo lectura) que calcule dicho valor y no sea preciso hacerlo cada vez.

Miembros para publicar resumenes de estadísticas.

Ya puestos a publicar, nada cuesta crear un método que devuelva formateados los resúmenes de los contadores, así poco o nada tendremos que hacer para tener una información aproximada.

Resumen formateado de estadísticas.

Ni que decir tiene que tendremos que poder resetear todos los contadores (por ejemplo cuando pasamos de testear una implementación a otra).

Resetear las estadísticas.

Constructores de la clase

Como veremos, la forma más habitual en la que se construirán los objetos de tipo Real en nuestros programas será mediante la conversión implícita, mucho más cómoda; sin embargo, no cuesta nada y parece más correcto crear algunos. Puesto que los vamos a definir, aquí incrementaremos los contadores de conversión.

Constructores de la clase.

Como vemos, utilizamos la definición del compilador DEBUG que nos indica cuando se está compilando para depuración, no obstante si quisiéramos contabilizar también las operaciones en una compilación release no cuesta nada cambiar el nombre por cualquier otro, lo que pasa es que será habitual querer sólo las estadísticas en un entorno de desarrollo en el que se prueban los algoritmos.

Conversiones de tipo

Algunas conversiones de tipo son implementadas mediante métodos en la clase, como los constructores de antes, no son necesarios, pero dejan más claro y aglutinan otras conversiones menos claras. Tamibién éstas serán las encargadas de incrementar los contadores de conversión.

Conversiones de tipo.

Unas conversiones mucho más interesantes son las siguientes:

Conversiones de tipo implícitos hacia el tipo Real.

Que las tres primeras conversiones sean implícitas tiene sentido mientras la dirección en la que va la conversión sea sin pérdida (y/o sin un coste computacional elevado), en otro caso, como en la conversión desde una cadena (en la que se puede producir un problema importante de rendimiento y claridad) las conversiones es mejor que sean explícitas, es decir, el programador si desea realizar ese tipo de conversiones debe indicar explícitamente que desea realizar ese tipo de conversión.

Similar ocurre con las conversiones en la otra dirección, en éste caso, sólo permitimos la conversión implícita del tipo en el que se almacena la información, pues en todos los casos existe pérdida de información o de rendimiento.

Conversiones de tipo implícitos desde el tipo Real.

Otras propiedades estáticas

La clase también es un buen sitio para publicar las constantes más importantes relacionadas con los Reales (de hecho así lo hace la clase estática del Framework .NET).

Propiedades relacionadas.

Propiedades

Para cada instancia, publicamos algunas propiedades interesantes que de las que también deberemos realizar alguna contabilidad como el valor absoluto, si es "casi" cero, si es positivo, etc.

Propiedades.

Funciones

Algo poco habitual pero que me parece interesante es que, ya que definimos la clase para cada instancia, las funciones más comunes que la transforma estén en la misma clase (lo contrario que en el Framework .NET). Así definimos dentro de la misma clase funciones como coseno, seno, tangente, etc.

Funciones de la clase.

Operadores

Pero sin duda, la parte más importante de nuestra clase tiene que ver con la sobrecarga de los operadores que utilizamos normalmente para realizar las operaciones matemáticas (suma, resta, multiplicación, etc.). Y lógicamente las dejaremos contabilizadas en sus respectivos contadores.

Por un lado tenemos los operadores unarios, aquellos en los que sólo interviene una instancia.

Operadores unarios sobrecargados.

Y por el otro, tenemos los operadores binarios, aquellos en los que intervienen dos instancias.

Operadores binarios sobrecargados.

¡Y ya tenemos nuestra clase lista!.

Nota importante sobre los operadores sobrecargados

Desgraciadamente, no es habitual que se pueda especificar la precedencia de los operadores sobrecargados. Concretamente en C# no es posible establecer la precedencia de operadores y no es posible por tanto sobrecargar el operador ^ de forma que tenga mayor prioridad que el producto y la división (sí se puede sobrecargar, pero no forzar una prioridad de precedencia mayor). En C# la tabla de precedencia es la siguiente (al menos en Visual C# 2005 versión 8.0):

Precedencia de operadores en C#.

Aunque podríamos usar otros operadores con mayor prioridad (como []) pierde el sentido de claridad y por tanto es mejor usar un método como x.Pow( y ).

Usar la clase

Utilizar nuestra clase dentro de cualquier programa es trivial, podemos usarla exactamente de la misma forma que utilizamos los tipos básicos double o float. Este es un ejemplo trivial y su salida:

Prueba de la clase con sobrecarga de operadores.

Otras cosas por hacer

No se han terminado aún todas las posibilidades de esta clase, cualquier cosa que esté directamente relacionada con los Reales es susceptible de ser incluida en la clase, la creación de los siguientes métodos parecen buenas ideas:


Sobrecarga de operadores descargar código fuente de /> Sobrecarga de operadores.
Opinado el 23/05/09 02:52, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    bien
Opinado el 29/09/10 23:52, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    me vale
Opinado el 29/09/11 17:40, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    no nos resolvio nuestra duda!!!! necesitamossaber
¿Te ha gustado? ¡aporta tu opinión!
Valoración:
 0    1    2    3    4    5    6    7    8    9    10

Comentario:
NOTA: si es una petición... ¡pon el e-mail al que responderte o no sabré a dónde escribir!

Código de verificación captcha