¿Hubiera sido posible? Sí. Pero hay muchos problemas con eso.
Considérese, por ejemplo, que almacena Java referencias a BigInteger, que en realidad está asignada en el montón, pero almacenar int literales. La diferencia puede ser dejado claro en C:
int i;
BigInt* bi;
Ahora, para ir automáticamente a partir de un literal con una referencia, uno necesariamente tiene que realizar anotaciones en el literal de alguna manera. Por ejemplo, si se estableció el bit más alto de la int, entonces los otros bits podrían usarse como una búsqueda de tabla de algún tipo para recuperar la referencia adecuada. Eso también significa que obtendrá un BigInt** bi
cada vez que se desborde en eso.
Por supuesto, ese es el bit que generalmente se utiliza para firmar, y las instrucciones de hardware dependen bastante de ello. Peor aún, si hacemos eso, entonces el hardware no podrá detectar el desbordamiento y configurará los indicadores para indicarlo. Como resultado, cada operación debería ir acompañada de alguna prueba para ver si el desbordamiento ha ocurrido o sucederá (dependiendo de cuándo se puede detectar).
Todo eso agregaría una gran cantidad de sobrecarga a la aritmética de enteros básicos, lo que en la práctica anularía los beneficios que tenía que comenzar. En otras palabras, es más rápido asumir que BigInt que tratar de usar int y detectar condiciones de desbordamiento, al mismo tiempo que se hace malabares con el problema de referencia/literal.
Por lo tanto, para obtener una ventaja real, uno tendría que usar más espacio para representar las entradas. Entonces, en lugar de almacenar 32 bits en la pila, en los objetos o en cualquier otro lugar donde los usemos, almacenamos 64 bits, por ejemplo, y usamos los 32 bits adicionales para controlar si queremos una referencia o un literal. Eso podría funcionar, pero hay un problema obvio: uso del espacio. :-) Sin embargo, podríamos ver más con hardware de 64 bits.
Ahora, puede preguntar por qué no solo 40 bits (32 bits + 1 byte) en lugar de 64? Básicamente, en el hardware moderno es preferible almacenar cosas en incrementos de 32 bits por razones de rendimiento, por lo que de todos modos, se rellenarán 40 bits a 64 bits.
EDITAR
Vamos a considerar cómo se puede ir haciendo esto en C#. Ahora, no tengo experiencia en programación con C#, así que no puedo escribir el código para hacerlo, pero espero poder dar una visión general.
La idea es crear una estructura para ello. Debe tener un aspecto más o menos así:
public struct MixedInt
{
private int i;
private System.Numeric.BigInteger bi;
public MixedInt(string s)
{
bi = BigInteger.Parse(s);
if (parsed <= int.MaxValue && parsed => int.MinValue)
{
i = (int32) parsed;
bi = 0;
}
}
// Define all required operations
}
lo tanto, si el número está en el rango de números enteros que utilizamos int, de lo contrario utilizamos BigInteger. Las operaciones deben garantizar la transición de uno a otro según sea necesario/posible. Desde el punto de vista del cliente, esto es transparente. Es solo un tipo MixedInt, y la clase se ocupa de usar lo que mejor se adapte.
Tenga en cuenta, sin embargo, que este tipo de optimización bien puede ser parte de C# BigInteger ya, dada su implementación como una estructura.
Si Java tuviera algo así como la estructura de C#, podríamos hacer algo como esto en Java también.
(O usa Python, Ruby, Jython o JRuby) –
(-1) Está más relacionado con el tipado estático frente al tipado dinámico, en lugar de primitivo frente a no primitivo. – ewernli
@ewemli Con el tipado estático todavía podrías hacer el cambio dentro de una clase contenedora (usando subtipos ocultos), pero estoy de acuerdo en que el tipado dinámico lo hace más fácil. Tener primitivos niega la capacidad de usar una clase contenedora. –