¿Cómo usar el entero sin firmar en Java 8 y Java 9?

81

En la página "Tipos de datos primitivos" de Oracle , se menciona que Java 8 agrega soporte para entradas y longs sin firmar:

int: De forma predeterminada, el inttipo de datos es un entero de complemento a dos con signo de 32 bits, que tiene un valor mínimo de −2 31 y un valor máximo de 2 31 −1. En Java SE 8 y versiones posteriores, puede utilizar el inttipo de datos para representar un entero de 32 bits sin signo, que tiene un valor mínimo de 0 y un valor máximo de 2 32 −1. Use la Integerclase para usar intel tipo de datos como un entero sin signo. Consulte la sección Las clases de números para obtener más información. Se han agregado métodos estáticos como compareUnsigned, divideUnsignedetc. a la Integerclase para admitir las operaciones aritméticas para enteros sin signo.

long: El longtipo de datos es un entero de complemento a dos de 64 bits. El signo longtiene un valor mínimo de −2 63 y un valor máximo de 2 63 −1. En Java SE 8 y posterior, puede utilizar el longtipo de datos para representar un 64 bits sin firmar long, que tiene un valor mínimo de 0 y un valor máximo de 2 64 −1. Utilice este tipo de datos cuando necesite un rango de valores más amplio que los proporcionados por int. La Longclase también contiene métodos como compareUnsigned, divideUnsignedetc. para admitir operaciones aritméticas para unsigned long.

Sin embargo, no encuentro forma de declarar un número entero o largo sin firmar. El siguiente código, por ejemplo, da un mensaje de error del compilador de "el literal está fuera de rango" (estoy usando Java 8, por supuesto), cuando debería estar dentro del rango (el valor asignado es precisamente 2 64 -1) :

public class Foo {
    static long values = 18446744073709551615L;
    
    public static void main(String[] args){
        System.out.println(values);
    }  
}

Entonces, ¿hay alguna forma de declarar un int o long sin firmar?

Pabce
fuente
2
¿Qué se devuelve en su constante Long.MAX_VALUE en java 8?
Bruno Franco
21
No hay un entero sin signo ni un tipo largo sin signo. Si usa uno de los nuevos métodos, ese método tratará un entero de 32 o 64 bits como si no estuviera firmado. Pero eso es todo. El tipo de variable seguirá estando firmado y depende de usted recordar que la está utilizando como un número sin firmar. No agregaron literales sin firmar, pero tal vez los agreguen a Java 9 si suficientes personas los molestan. :)
ajb
5
Realmente no cambiaron nada, aparte de agregar los nuevos métodos.
Hot Licks
4
Por lo que puedo decir, todo lo que hicieron fue agregar métodos que pueden devolver valores sin firmar, pero no le permiten declarar valores sin firmar. Un poco tonto si me preguntas, y un verdadero dolor. Me pregunto si una forma sería usar Integer.divideUnsigned, con un parámetro siendo 1, el otro es cualquier número que desee que se trate como sin firmar. Funcionaría hasta donde yo sé, pero parece ser una forma realmente tonta de hacer las cosas.
pista el
@ajb, ¿cómo puedo ser de ayuda en el proceso de "corrección de errores" para ver finalmente sin firmar correctamente en Java? :)
Matthieu

Respuestas:

51

Según la documentación que publicó y esta publicación de blog , no hay diferencia al declarar la primitiva entre un int / long sin firmar y uno firmado. El "nuevo soporte" es la adición de los métodos estáticos en las clases Integer y Long, por ejemplo, Integer.divideUnsigned . Si no está utilizando esos métodos, su "unsigned" por encima de 2 ^ 63-1 es simplemente un largo con un valor negativo.

De un vistazo rápido, no parece que haya una manera de declarar constantes enteras en el rango fuera de +/- 2 ^ 31-1, o +/- 2 ^ 63-1 para longs. Tendría que calcular manualmente el valor negativo correspondiente a su valor positivo fuera de rango.

Sbodd
fuente
85

Bueno, incluso en Java 8, longy inttodavía están firmados, solo algunos métodos los tratan como si no estuvieran firmados . Si desea escribir un longliteral sin firmar de esa manera, puede hacerlo

static long values = Long.parseUnsignedLong("18446744073709551615");

public static void main(String[] args) {
    System.out.println(values); // -1
    System.out.println(Long.toUnsignedString(values)); // 18446744073709551615
}
kajacx
fuente
5
Una observación que veo con esto es que el orden está dañado. Si tuviera que obtener un long sin firmar de algún sistema (Twitter proporciona tales ID, por cierto) y quisiera ordenarlos, tal vez porque sabía que eran cronológicos, entonces todo lo que esté más allá de Long.MAX_VALUE en realidad aparecerá antes de 0 como negativo con la implementación de Java :-(. En su lugar, parece que uno también debe desplazarlo hacia abajo de modo que el 0 sin firmar se asigne a Long.MIN_VALUE firmado.
David Smiley
10
@DavidSmiley Buen punto. Para ordenar long sin firmar, use Long.compareUnsigned , o pase ese método como un comparador para ordenar funciones,
kajacx
26
    // Java 8
    int vInt = Integer.parseUnsignedInt("4294967295");
    System.out.println(vInt); // -1
    String sInt = Integer.toUnsignedString(vInt);
    System.out.println(sInt); // 4294967295

    long vLong = Long.parseUnsignedLong("18446744073709551615");
    System.out.println(vLong); // -1
    String sLong = Long.toUnsignedString(vLong);
    System.out.println(sLong); // 18446744073709551615

    // Guava 18.0
    int vIntGu = UnsignedInts.parseUnsignedInt(UnsignedInteger.MAX_VALUE.toString());
    System.out.println(vIntGu); // -1
    String sIntGu = UnsignedInts.toString(vIntGu);
    System.out.println(sIntGu); // 4294967295

    long vLongGu = UnsignedLongs.parseUnsignedLong("18446744073709551615");
    System.out.println(vLongGu); // -1
    String sLongGu = UnsignedLongs.toString(vLongGu);
    System.out.println(sLongGu); // 18446744073709551615

    /**
     Integer - Max range
     Signed: From −2,147,483,648 to 2,147,483,647, from −(2^31) to 2^31 – 1
     Unsigned: From 0 to 4,294,967,295 which equals 2^32 − 1

     Long - Max range
     Signed: From −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, from −(2^63) to 2^63 − 1
     Unsigned: From 0 to 18,446,744,073,709,551,615 which equals 2^64 – 1
     */
blueberry0xff
fuente
9
Debería agregar alguna explicación, no solo lanzar código allí.
pista el
20

No hay forma de declarar un unsigned long o int en Java 8 o Java 9. Pero algunos métodos los tratan como si estuvieran sin firmar, por ejemplo:

static long values = Long.parseUnsignedLong("123456789012345678");

pero esta no es una declaración de la variable.

1ac0
fuente
3
Me pregunto por qué no. podrían haber agregado fácilmente tipos uint y ulong
EKanadily
@docesam en este y este documento es una explicación detallada. en resumen, solo se han agregado métodos para la manipulación sin firmar a la clase Integer. y si podrían haber agregado fácilmente , no sé por qué no lo harán ;-)
1ac0
@ Ladislav DANKO hay gente prudente y hay otros muchachos. No estoy seguro de si, en este caso, fue prudente o no, pero tengo la impresión de que Oracle no es tan prudente.
EKanadily
1
@docesam No, no podrían haber "agregado fácilmente tipos uint y ulong". Eso requeriría revisar el JLS, la especificación JVM, JNI, muchas bibliotecas (como java.util.Arrays), la reflexión y mucho más.
Nayuki
1
@ Michaelangel007 Hola, creo que estamos abordando este tema con diferentes suposiciones de fondo. ¿Quieres enviarme un correo electrónico y hablaremos de los detalles?
Nayuki
4

Si el uso de una biblioteca de terceros es una opción, existe jOOU (una biblioteca derivada de jOOQ ), que ofrece tipos de envoltura para números enteros sin signo en Java. Eso no es exactamente lo mismo que tener soporte de tipo primitivo (y por lo tanto código de bytes) para tipos sin firmar, pero quizás aún sea lo suficientemente bueno para su caso de uso.

import static org.joou.Unsigned.*;

// and then...
UByte    b = ubyte(1);
UShort   s = ushort(1);
UInteger i = uint(1);
ULong    l = ulong(1);

Todos estos tipos se extienden java.lang.Numbery pueden convertirse en tipos primitivos de orden superior y BigInteger.

(Descargo de responsabilidad: trabajo para la empresa detrás de estas bibliotecas)

Lukas Eder
fuente