Declarando un int sin firmar en Java

317

¿Hay alguna manera de declarar un int sin firmar en Java?

O la pregunta también puede enmarcarse así: ¿Cuál es el equivalente Java de unsigned?

Solo para decirte el contexto en el que estaba mirando la implementación de Java String.hashcode(). Quería probar la posibilidad de colisión si el entero fuera 32 unsigned int.

Harshdeep
fuente
77
No hay tipos sin firmar en Java.
Andrew Logvinov
1
Esta publicación puede ayudarte a stackoverflow.com/a/4449161/778687
tusar el
2
No parece ser una forma de AFACIO. Relacionado: stackoverflow.com/questions/430346/…
James Manning
11
Depende del propósito que intentes lograr. Para la mayoría de los propósitos, todos los enteros en Java están firmados. Sin embargo, puede tratar un entero con signo como sin signo en un caso específico: puede desplazarse a la derecha sin extender el signo utilizando el >>>operador en lugar de >>.
dasblinkenlight

Respuestas:

311

Java no tiene un tipo de datos para enteros sin signo .

Puede definir un en longlugar de un intsi necesita almacenar valores grandes.

También puede usar un entero con signo como si no estuviera firmado. El beneficio de la representación del complemento a dos es que la mayoría de las operaciones (como suma, resta, multiplicación y desplazamiento a la izquierda) son idénticas a nivel binario para enteros con y sin signo. Sin embargo, algunas operaciones (división, desplazamiento a la derecha, comparación y fundición) son diferentes. A partir de Java SE 8, los nuevos métodos en la Integerclase le permiten utilizar completamente el inttipo de datos para realizar operaciones aritméticas sin signo :

En Java SE 8 y versiones posteriores, puede usar el tipo de datos int 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 clase Integer para usar el tipo de datos int como un entero sin signo. Se han agregado métodos estáticos como compareUnsigned, divideUnsignedetc. a la clase Integer para admitir las operaciones aritméticas para enteros sin signo.

Tenga en cuenta que las intvariables todavía están firmadas cuando se declaran, pero ahora es posible utilizar la aritmética sin signo utilizando esos métodos en la Integerclase.

Simeon Visser
fuente
11
Para ser justos, para muchos proyectos los requisitos técnicos no son tan estrictos y de hecho puede permitirse el lujo de "desperdiciar" la memoria de esa manera.
Simeon Visser
66
Lo sé, también entiendo el propósito original de Java. Pero, por ejemplo, los teléfonos inteligentes no disponen de memoria adicional. Y usualmente usan Java, que yo sepa. Pero bueno, no quiero comenzar una guerra entre los programadores de Java y los demás.
Tomáš Zato - Restablece a Monica el
122
Para mí esto no es solo un caso de malgastar dinero. Cuando trabajas en un nivel de bits, sin firmar es simplemente más fácil de trabajar
Cruncher
24
A partir de Java 8, esto ya no es cierto . En Java SE 8 y versiones posteriores, puede usar el inttipo de datos para representar un entero de 32 bits sin signo, que tiene un valor mínimo 0y un valor máximo de 2^32-1. - ver docs.oracle.com/javase/tutorial/java/nutsandbolts/… y docs.oracle.com/javase/8/docs/api/java/lang/Integer.html
8bitjunkie
2
@ 7SpecialGems: He actualizado la respuesta para incluir esa información. Dicho esto, no es posible declarar enteros sin signo o excluir valores negativos, solo es posible usar un intcomo si no estuviera firmado usando varios métodos.
Simeon Visser
70

¡Hay una API para Entero sin signo y Largo en Java 8!

baraber
fuente
1
@stas Tengo dificultades para comprender el uso y el gran problema de tener la capacidad de usar tipos de datos sin firmar. De las diversas fuentes en línea de las que leí, parece que gira en torno a solo ampliar el valor máximo, y lo implícito por naturaleza garantiza que es un número positivo. ¿Es correcto mi entendimiento o hay otras razones importantes? Además, ahora que la Integerclase en Java 8 permite usar int sin signo, es la diferencia entre solo en el espacio y la velocidad (ya que en C / C ++ son primitivos, mientras que en Java, es un contenedor de objetos completo)
Abdul
2
@Abdul: cuando trabaja a nivel de bits (generalmente porque está interactuando con el hardware) necesita valores para comportarse de cierta manera. es decir, pasar después de 11111111 a 00000000, etc. El uso de firmado en lugar de no firmado puede interrumpir los cálculos de CRC, etc. No es un obstáculo, solo el tiempo perdido.
Lorne K
3
@ Lorne K: en Java, ints se desplazan , incluso si están firmados. Es C / C ++ donde se reinicia sin firmar, pero si se firma causa "Comportamiento indefinido" en el desbordamiento. Si "pasar" es su única preocupación, no necesita firmar. Supongo que es por eso que las rutinas CRC, etc. funcionan en Java sin esfuerzos adicionales. Y es por eso que la nueva API solo agrega análisis, formato, comparaciones, división y resto. Todas las demás operaciones, es decir, todas las manipulaciones de bits, pero también la suma, resta, multiplicación, etc., están haciendo lo correcto de todos modos.
Holger
44
@Ciprian Tomoiaga: para agregar con rollover, los patrones de bits de la entrada y el resultado no dependen de si lo interpreta como un número con signo o un número sin signo. Si tiene paciencia, puede probarlo con todas las combinaciones 2⁶⁵ ...
Holger
3
@ Holger gracias por la explicación! De hecho, resulta que por eso en realidad usamos el complemento de 2. Lo probé con algunas combinaciones de 2 ^ 8 ^^
Ciprian Tomoiagă
66

Si un valor en un int es con signo o sin signo depende de cómo se interpreten los bits: Java interpreta los bits como un valor con signo (no tiene primitivas sin signo).

Si tiene un int que desea interpretar como un valor sin signo (por ejemplo, si lee un int de un DataInputStream que sabe que contiene un valor sin signo), puede hacer el siguiente truco.

int fourBytesIJustRead = someObject.getInt();
long unsignedValue = fourBytesIJustRead & 0xffffffffl;

Tenga en cuenta que es importante que el literal hexadecimal sea un literal largo, no un literal int, de ahí la 'l' al final.

Zsolt Safrany
fuente
3
Para mí, esta es la mejor respuesta ... Mis datos provienen de un UID de tarjeta NFC, que puede tener 4 u 8 bytes ... En el caso de 4 bytes, necesitaba enviarlo a un int sin firmar, y no pude use ByteBuffer.getLong porque no eran datos de 64 bits. Gracias.
Loudenvier
¿Por qué tiene que ser largo? ¿No puedes simplemente hacer 0xFFFFFFy mantener el int?
Displee
19

Necesitábamos números sin signo de modelar de MySQL sin signo TINYINT, SMALLINT, INT, BIGINTen jOOQ , por lo que hemos creado Joou , una oferta biblioteca minimalista envoltorio tipos de números enteros sin signo en Java. Ejemplo:

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 se pueden convertir en tipos primitivos y de orden superior BigInteger. Espero que esto ayude.

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

Lukas Eder
fuente
¡Esto suena muy conveniente! Gracias por mencionar :)
Lucas Sousa el
7

Para números sin signo, puede usar estas clases de la biblioteca Guava :

Apoyan varias operaciones:

  • más
  • menos
  • veces
  • modificación
  • dividido por

Lo que parece faltar en este momento son los operadores de cambio de bytes. Si los necesita, puede usar BigInteger de Java.

Andrejs
fuente
2

Quizás esto es lo que quisiste decir?

long getUnsigned(int signed) {
    return signed >= 0 ? signed : 2 * (long) Integer.MAX_VALUE + 2 + signed;
}
  • getUnsigned(0) → 0
  • getUnsigned(1) → 1
  • getUnsigned(Integer.MAX_VALUE) → 2147483647
  • getUnsigned(Integer.MIN_VALUE) → 2147483648
  • getUnsigned(Integer.MIN_VALUE + 1) → 2147483649
Matthias Ronge
fuente
Estás sacrificando una millonésima parte de un segundo de tiempo de rendimiento por escribir de forma diferida con operadores ternarios en lugar de sentencias if. No está bien. (broma)
ytpillai
55
¿De verdad crees que 2 * (long) Integer.MAX_VALUE + 2es más fácil de entender que 0x1_0000_0000L? En ese sentido, ¿por qué no simplemente return signed & 0xFFFF_FFFFL;?
Holger
2

Parece que puede manejar el problema de firma haciendo un "AND lógico" en los valores antes de usarlos:

Ejemplo (valor de byte[] header[0]is 0x86):

System.out.println("Integer "+(int)header[0]+" = "+((int)header[0]&0xff));

Resultado:

Integer -122 = 134
Carsten Semark
fuente
2

Aquí hay buenas respuestas, pero no veo ninguna demostración de operaciones bit a bit. Como dice Visser (la respuesta actualmente aceptada), Java firma enteros por defecto (Java 8 tiene enteros sin signo, pero nunca los he usado). Sin más preámbulos, hagámoslo ...

RFC 868 Ejemplo

¿Qué sucede si necesita escribir un entero sin signo en IO? Un ejemplo práctico es cuando desea generar el tiempo de acuerdo con RFC 868 . Esto requiere un número entero de 32 bits, big-endian, sin signo que codifique el número de segundos desde las 12:00 AM del 1 de enero de 1900. ¿Cómo codificaría esto?

Haga su propio entero de 32 bits sin signo como este:

Declarar una matriz de bytes de 4 bytes (32 bits)

Byte my32BitUnsignedInteger[] = new Byte[4] // represents the time (s)

Esto inicializa la matriz, consulte ¿Las matrices de bytes se inicializan a cero en Java? . Ahora tiene que llenar cada byte en la matriz con información en el orden big-endian (o little-endian si quiere causar estragos). Suponiendo que tiene un tiempo que contiene el tiempo (los enteros largos son de 64 bits en Java) llamado secondsSince1900(que solo utiliza los primeros 32 bits, y ha manejado el hecho de que Date hace referencia a las 12:00 AM del 1 de enero de 1970), luego puede usar el AND lógico para extraer bits de él y cambiar esos bits a posiciones (dígitos) que no se ignorarán cuando se conviertan en un Byte, y en orden big-endian.

my32BitUnsignedInteger[0] = (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24); // first byte of array contains highest significant bits, then shift these extracted FF bits to first two positions in preparation for coersion to Byte (which only adopts the first 8 bits)
my32BitUnsignedInteger[1] = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
my32BitUnsignedInteger[2] = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
my32BitUnsignedInteger[3] = (byte) ((secondsSince1900 & 0x00000000000000FFL); // no shift needed

Nuestro my32BitUnsignedIntegerahora es equivalente a un entero big endian sin signo de 32 bits que se adhiere al estándar RCF 868. Sí, el tipo de datos largo está firmado, pero ignoramos ese hecho, porque asumimos que los segundosSince1900 solo usaron los 32 bits inferiores). Debido a convertir el largo en un byte, se ignorarán todos los bits superiores a 2 ^ 7 (primeros dos dígitos en hexadecimal).

Fuente referenciada: Java Network Programming, cuarta edición.

Jonathan Komar
fuente
1

Acabo de hacer este código, que convierte "this.altura" de negativo a positivo. Espero que esto ayude a alguien que lo necesita

       if(this.altura < 0){    

                        String aux = Integer.toString(this.altura);
                        char aux2[] = aux.toCharArray();
                        aux = "";
                        for(int con = 1; con < aux2.length; con++){
                            aux += aux2[con];
                        }
                        this.altura = Integer.parseInt(aux);
                        System.out.println("New Value: " + this.altura);
                    }
Rómulo
fuente
-19

Puede usar la función Math.abs (número). Devuelve un número positivo.

kyo722
fuente
12
nitpick: no si pasasMIN_VALUE
Dennis Meng
2
@ kyo722 No puedo imaginar que esto devolverá un valor positivo en el rango de primitivas sin signo.
Florian R. Klein
1
nitpick # 2: no si pasas0
genisage