Declarando um int não assinado em Java

316

Existe uma maneira de declarar um int não assinado em Java?

Ou a pergunta também pode ser enquadrada da seguinte maneira: Qual é o equivalente em Java de não assinado?

Só para dizer o contexto que eu estava observando na implementação do Java String.hashcode(). Eu queria testar a possibilidade de colisão se o número inteiro fosse 32 int sem sinal.

Harshdeep
fonte
7
Não há tipos não assinados em Java.
Andrew Logvinov 24/03
1
Esta postagem pode ajudá-lo a stackoverflow.com/a/4449161/778687
tusar:
2
Não parece ser uma maneira AFAICT. Veja também: stackoverflow.com/questions/430346/…
James Manning
11
Depende do objetivo que você está tentando alcançar. Para a maioria dos propósitos, todos os números inteiros em Java são assinados. No entanto, você pode tratar um número inteiro assinado como não assinado em um caso específico: você pode mudar para a direita sem estender o sinal usando o >>>operador em vez de >>.
dasblinkenlight

Respostas:

310

Java não possui um tipo de dados para números inteiros não assinados .

Você pode definir um em longvez de um intse precisar armazenar valores grandes.

Você também pode usar um número inteiro assinado como se não estivesse assinado. O benefício da representação de complemento de dois é que a maioria das operações (como adição, subtração, multiplicação e deslocamento à esquerda) são idênticas em nível binário para números inteiros assinados e não assinados. Algumas operações (divisão, mudança à direita, comparação e elenco), no entanto, são diferentes. No Java SE 8, novos métodos na Integerclasse permitem usar totalmente o inttipo de dados para executar aritmética não assinada :

No Java SE 8 e posterior, é possível usar o tipo de dados int para representar um número inteiro de 32 bits não assinado, que possui um valor mínimo de 0 e um valor máximo de 2 ^ 32-1. Use a classe Integer para usar o tipo de dados int como um número inteiro não assinado. Os métodos estáticos, como compareUnsigned, divideUnsignedetc foram adicionados à classe Integer para apoiar as operações aritméticas em números inteiros sem sinal.

Observe que as intvariáveis ​​ainda são assinadas quando declaradas, mas a aritmética não assinada agora é possível usando esses métodos na Integerclasse.

Simeon Visser
fonte
11
Para ser justo, em muitos projetos, os requisitos técnicos não são tão rígidos e você pode, de fato, dar ao luxo de "desperdiçar" memória assim.
Simeon Visser
6
Eu sei, eu também entendo o propósito original do Java. Mas, por exemplo, os smartphones não descartam memória extra. E eles costumam usar Java, tanto quanto eu sei. Mas bem, não quero começar uma guerra entre programadores Java e os outros.
Tomáš Zato - Restabelece Monica
122
Para mim, este não é apenas um caso de desperdiçar dinheiro. Quando você trabalha em um nível pouco, não assinado é simplesmente mais fácil trabalhar com
Cruncher
24
A partir do Java 8, isso não é mais verdade . No Java SE 8 e posterior, você pode usar o inttipo de dados para representar um número inteiro de 32 bits não assinado, que possui um valor mínimo de 0e um valor máximo de 2^32-1. - consulte docs.oracle.com/javase/tutorial/java/nutsandbolts/… e docs.oracle.com/javase/8/docs/api/java/lang/Integer.html
8bitjunkie
2
@ 7SpecialGems: atualizei a resposta para incluir essas informações. Dito isto, não é possível declarar números inteiros não assinados ou excluir valores negativos; só é possível usar um intcomo se não fosse assinado usando vários métodos.
Simeon Visser
70

Há uma API para Inteiro e Longo sem sinal no Java 8!

baraber
fonte
1
@stas Estou tendo dificuldade em entender o uso e grande coisa por ter a capacidade de usar tipos de dados não assinados. Das várias fontes on-line que li, parece que ele gira em torno de apenas aumentar o valor máximo, e o implícito por natureza garante que seja um número positivo. Meu entendimento está correto ou existem outros motivos principais? Além disso, agora que a Integerclasse em Java 8 permite a utilização de unsigned int, é a diferença entre apenas espaço e velocidade (já que em C / C ++ são primitivas, enquanto em Java é um invólucro de objeto inteiro)
Abdul
2
@Abdul - quando você trabalha no nível de bits (geralmente porque está fazendo interface com o hardware), é necessário que os valores se comportem de uma certa maneira. isto é - rolar depois de 11111111 para 00000000 etc. Usar o sinal no lugar de não assinado pode interromper os cálculos de CRC etc. Não é uma parada de exibição, apenas perda de tempo.
Lorne K
3
@Lorne K: em Java, introlam, mesmo que estejam assinados. É em C / C ++ que o rolo não assinado passa, mas o sinal causa "Comportamento indefinido" no estouro. Se "rolar" é sua única preocupação, você não precisa de assinatura. Eu acho que é por isso que as rotinas CRC etc. funcionam em Java sem esforços extras. E é por isso que a nova API adiciona apenas análise, formatação, comparações, divisão e restante. Todas as outras operações, nomeadamente todas as manipulações de bits, mas também adição, subtração, multiplicação, etc., estão fazendo a coisa certa de qualquer maneira.
Holger
4
@Ciprian Tomoiaga: para adicionar com sobreposição, os padrões de bits da entrada e o resultado não dependem de você interpretá-lo como número assinado ou não assinado. Se você tiver paciência, você pode experimentá-lo com todos os 2⁶⁵ combinações ...
Holger
3
@ Holger obrigado pela explicação! De fato, é por isso que usamos o complemento de 2. Eu tentei isso com alguns 2 ^ 8 combinações ^^
Ciprian Tomoiagă
66

Se um valor em um int é assinado ou não assinado depende de como os bits são interpretados - Java interpreta os bits como um valor assinado (ele não possui primitivas não assinadas).

Se você tem um int que deseja interpretar como um valor não assinado (por exemplo, você lê um int de um DataInputStream que você sabe que contém um valor não assinado), pode executar o seguinte truque.

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

Observe que é importante que o literal hexadecimal seja um literal longo, não um literal int - daí o 'l' no final.

Zsolt Safrany
fonte
3
Para mim, esta é a melhor resposta ... Meus dados vêm de um UID do cartão NFC, que pode ter 4 ou 8 bytes ... No caso de 4 bytes, eu precisava convertê-lo em um int sem sinal e não podia use ByteBuffer.getLong porque não eram dados de 64 bits. Obrigado.
Loudenvier
Por que precisa ser longo? Você não pode simplesmente fazer 0xFFFFFFe manter o int?
Displee
19

Precisávamos números não assinados para modelar do MySQL não assinado TINYINT, SMALLINT, INT, BIGINTem jOOQ , que é por isso que criamos jOOU , uma oferta biblioteca minimalista invólucro tipos para números inteiros não assinados em Java. Exemplo:

import static org.joou.Unsigned.*;

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

Todos esses tipos se estendem java.lang.Numbere podem ser convertidos em tipos primitivos e de ordem superior BigInteger. Espero que isto ajude.

(Aviso: trabalho para a empresa por trás dessas bibliotecas)

Lukas Eder
fonte
Isso parece muito conveniente! Obrigado por mencionar. :)
Lucas Sousa
7

Para números não assinados, você pode usar estas classes da biblioteca Guava :

Eles suportam várias operações:

  • mais
  • menos
  • vezes
  • mod
  • dividido por

O que parece estar faltando no momento são os operadores de troca de bytes. Se você precisar, pode usar o BigInteger a partir de Java.

Andrejs
fonte
4

Use charpara números inteiros não assinados de 16 bits.

Steven Stewart-Gallus
fonte
Char não é um sinal int de 32 bits, mas char é uma boa resposta para ganho de memória. Este link: stackoverflow.com/questions/1841461/unsigned-short-in-java (de jqr acima)
blobmaster
2

Talvez seja isso que você quis dizer?

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
fonte
Você está sacrificando um zilhão de segundos de tempo de desempenho para digitar com preguiça com operadores ternários, em vez de instruções if. Não é bom. (brincadeira)
ytpillai
5
Você realmente acha que 2 * (long) Integer.MAX_VALUE + 2é mais fácil entender do que 0x1_0000_0000L? A esse respeito, por que não simplesmente return signed & 0xFFFF_FFFFL;?
Holger
2

Parece que você pode lidar com o problema de assinatura executando um "AND lógico" nos valores antes de usá-los:

Exemplo (o valor de byte[] header[0]é 0x86):

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

Resultado:

Integer -122 = 134
Carsten Semark
fonte
2

Há boas respostas aqui, mas não vejo nenhuma demonstração de operações bit a bit. Como Visser (a resposta atualmente aceita) diz, o Java assina números inteiros por padrão (o Java 8 tem números inteiros não assinados, mas eu nunca os usei). Sem mais delongas, vamos fazê-lo ...

Exemplo RFC 868

O que acontece se você precisar gravar um número inteiro não assinado no IO? Um exemplo prático é quando você deseja exibir o tempo de acordo com a RFC 868 . Isso requer um número inteiro não assinado de 32 bits, big endian, que codifica o número de segundos desde 00:00 de 1º de janeiro de 1900. Como você codificaria isso?

Crie seu próprio número inteiro de 32 bits não assinado como este:

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

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

Isso inicializa a matriz, consulte As matrizes de bytes são inicializadas como zero em Java? . Agora você precisa preencher cada byte na matriz com informações na ordem big-endian (ou little-endian se você quiser causar estragos). Supondo que você tenha um longo contendo o tempo (inteiros longos têm 64 bits em Java) chamado secondsSince1900(que utiliza apenas os primeiros 32 bits, e você já lidou com o fato de que Date faz referência a 00:00 em 1º de janeiro de 1970), você pode usar o AND lógico para extrair bits dele e mudar esses bits para posições (dígitos) que não serão ignorados quando colocados em um byte e na ordem 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

my32BitUnsignedIntegerAgora, nosso é equivalente a um número inteiro big endian não assinado de 32 bits que adere ao padrão RCF 868. Sim, o tipo de dados longo é assinado, mas ignoramos esse fato, porque assumimos que os segundosSince1900 usavam apenas os 32 bits inferiores). Por envolver o comprimento em um byte, todos os bits maiores que 2 ^ 7 (dois primeiros dígitos em hexadecimal) serão ignorados.

Fonte referenciada: Java Network Programming, 4ª Edição.

Jonathan Komar
fonte
1

Acabei de criar esse pedaço de código, que converte "this.altura" de número negativo em positivo. Espero que isso ajude alguém em necessidade

       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);
                    }
Romulo
fonte
-19

Você pode usar a função Math.abs (number). Retorna um número positivo.

kyo722
fonte
12
nitpick: não se você passar porMIN_VALUE
Dennis Meng
2
@ kyo722 Não consigo imaginar que isso retorne um valor positivo no intervalo de primitivas não assinadas.
Florian R. Klein
1
nitpick # 2: não se você 0
entrar