Pergunta

Por que não Java incluem suporte para inteiros sem sinal?

Parece-me ser uma omissão estranha, uma vez que eles permitem escrever código que é menos susceptível de produzir transborda na entrada inesperadamente grande.

Além disso, usando números inteiros sem sinal pode ser uma forma de auto-documentação, pois indicam que o valor que o int não assinado foi destinado a espera nunca é suposto ser negativo.

Por fim, em alguns casos, inteiros sem sinal pode ser mais eficiente para certas operações, como a divisão.

Qual é a desvantagem de incluir esses?

Foi útil?

Solução

Isto é de um href="http://www.gotw.ca/publications/c_family_interview.htm" entrevista com Gosling e outros , com simplicidade:

Gosling: Para mim, como um designer da linguagem, o que eu realmente não me considero estes dias, o "simples" realmente acabou significado era que eu poderia esperar J. aleatória desenvolvedor para manter a especificação em sua cabeça. Essa definição diz que, por exemplo, Java não é - e na verdade um monte de línguas acabar com um monte de casos de canto, coisas que ninguém realmente entende. Teste qualquer desenvolvedor C sobre sem assinatura, e logo você descobre que quase ninguém desenvolvedores C realmente entender o que se passa com não assinado, o que a aritmética não assinada é. Coisas como essa feita C complexa. A parte de linguagem de Java é, penso eu, muito simples. As bibliotecas que você tem que olhar para cima.

Outras dicas

Leitura entre as linhas, eu acho que a lógica era algo como isto:

  • em geral, os designers Java queria simplificar o repertório de tipos de dados disponíveis
  • para fins de diárias, eles sentiram que a necessidade mais comum era para tipos de dados assinados
  • de execução de determinados algoritmos, às vezes é necessário unsigned aritmética, mas o tipo de programadores que seria a implementação de tais algoritmos também teria o conhecimento para "trabalho round" fazendo unsigned aritmética com tipos de dados assinados

Na maior parte, eu diria que foi uma decisão razoável. Possivelmente, eu teria:

  • feita byte não assinados, ou pelo menos ter fornecido uma assinados alternativas / não assinados, possivelmente com nomes diferentes, para este tipo de dados (tornando-assinado é boa consistência, mas quando você precisar de um byte assinado?)
  • feito com a distância 'curto' (quando foi a última utilização de 16 bits assinado aritmética?)

Ainda assim, com um pouco de kludging, operações sobre valores não assinados até 32 bits não são tooo ruim, ea maioria das pessoas não precisa de divisão de 64 bits sem sinal ou comparação.

Esta é uma questão mais velho e pat fez menção brevemente char, eu apenas pensei que eu deveria expandir isso para outras pessoas que vão olhar para esta abaixo da estrada. Vamos dar uma olhada nos tipos primitivos Java:

byte - 8-bit inteiro assinado

short - 16-bit inteiro assinado

int - 32-bit inteiro assinado

long - 64-bit inteiro assinado

char - caracteres de 16 bits (inteiro sem sinal)

Embora char não suporta unsigned aritmética, ele essencialmente pode ser tratado como um inteiro unsigned. Você teria que explicitamente lançar operações aritméticas trás em char, mas isso não lhe fornecer uma maneira de especificar números unsigned.

char a = 0;
char b = 6;
a += 1;
a = (char) (a * b);
a = (char) (a + b);
a = (char) (a - 16);
b = (char) (b % 3);
b = (char) (b / a);
//a = -1; // Generates complier error, must be cast to char
System.out.println(a); // Prints ? 
System.out.println((int) a); // Prints 65532
System.out.println((short) a); // Prints -4
short c = -4;
System.out.println((int) c); // Prints -4, notice the difference with char
a *= 2;
a -= 6;
a /= 3;
a %= 7;
a++;
a--;

Sim, não há suporte direto para inteiros sem sinal (obviamente, eu não teria de lançar a maioria dos meus operações de backup em carvão se havia apoio directo). No entanto, não há, certamente, existir um tipo de dados primitivos sem sinal. Eu teria gostado de ter visto um byte não assinado, bem como, mas eu acho que duplicando o custo de memória e passar a usar carvão é uma opção viável.


Editar

Com JDK8 há novas APIs para Long e Integer que fornecem métodos auxiliares quando tratamento valores long e int como valores não assinados.

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

Além disso, Goiaba fornece uma série de métodos auxiliares para fazer coisas semelhantes para os tipos inteiros que ajuda a fechar a lacuna deixada pela falta de suporte nativo para inteiros unsigned.

Java tem tipos não assinados, ou pelo menos um: char é um curto sem sinal. Assim, qualquer desculpa Gosling joga-se é realmente apenas sua ignorância porque não há outros tipos não assinados.

Também tipos curtas: shorts são usados ??o tempo todo para multimídia. A razão é que você pode caber 2 amostras em um único 32 bits sem sinal longa e vetorizar muitas operações. A mesma coisa com dados de 8 bits e bytes não assinado. Você pode caber 4 ou 8 amostras em um registro para vectorizing.

Assim que assinado e ints não assinados são misturados em uma expressão coisas começam a ficar confuso e provavelmente você irá informações perder. Restringindo Java para ints assinados realmente só apura as coisas. Estou contente por eu não ter que se preocupar com todo o assinado negócio / sem assinatura, embora às vezes eu perca a 8ª bit em um byte.

http://skeletoncoder.blogspot.com/ 2006/09 / java-tutoriais-por-no-unsigned.html

Esse cara diz porque as operações padrão define C envolvendo ints não assinados e assinados que deverão ser tratados como não assinado. Isso poderia causar inteiros negativos assinado de rolar em uma grande int não assinado, podendo causar erros.

Eu acho que Java é bem como está, acrescentando unsigned iria complicar-lo sem grande lucro. Mesmo com o simplificado inteiro modelo, a maioria dos programadores Java não sei como os tipos numéricos básicos se comportam - basta ler o livro Puzzlers Java para ver o que equívocos que você pode segurar.

Como para conselhos práticos:

  • Se os seus valores são de tamanho um tanto arbitrária e não se encaixam em int, uso long. Se eles não se encaixam em uso long BigInteger.

  • Use os tipos menores apenas para matrizes quando você precisa economizar espaço.

  • Se você precisar exatamente 64/32/16/8 pedaços, uso long / int / short / byte e parar de se preocupar com o bit de sinal, excepto para a divisão, comparação, deslocamento para a direita, e de qualidade.

Veja também esta resposta sobre "portabilidade um gerador de números aleatórios de C para Java".

Com JDK8 ele tem algum suporte para eles.

Podemos ainda ver suporte completo de tipos não assinados em Java, apesar das preocupações de Gosling.

Eu sei que este post é muito antiga; no entanto, para o seu interesse, em Java 8 e posterior, você pode usar o tipo de dados int para representar um número inteiro de 32 bits sem sinal, que tem um valor mínimo de 0 e um valor máximo de 2 32 -1 . Use a classe Integer ao tipo de dados uso int como um inteiro sem sinal e métodos estáticos como compareUnsigned(), divideUnsigned() etc. foram adicionados à classe Integer para apoiar as operações aritméticas em números inteiros sem sinal.

Eu já ouvi histórias de que eles estavam a ser incluído perto da versão Java orignal. Oak foi o precursor do Java e, em alguns documentos de especificação não havia menção de valores usigned. Infelizmente, estas nunca fez isso para a linguagem Java. Tanto quanto qualquer um tem sido capaz de descobrir o que eles apenas não se implementado, provavelmente devido a uma restrição de tempo.

Uma vez eu tomei um C ++ curso com alguém no comitê de padrões de C ++ que implicou que Java fez a decisão certa para evitar ter inteiros sem sinal porque (1) a maioria dos programas que usam números inteiros sem sinal pode fazer tão bem com inteiros assinados e este é mais natural em termos de como as pessoas pensam, e (2) usando inteiros sem sinal resulta em lotes fáceis de criar, mas difícil de depurar problemas tais como integer overflow aritmético e perder bits significativos na conversão entre os tipos assinados e não assinados. Se, por engano subtrair 1 0 usando inteiros assinados, muitas vezes, mais rapidamente faz com que seu programa trave e torna mais fácil para encontrar o bug do que se envolve em torno de 2 ^ 32-1, e compiladores e ferramentas de análise estática e tempo de execução verifica que supor que você sabe o que está fazendo, pois você escolheu usar unsigned aritmética. Além disso, os números negativos como -1 muitas vezes pode representar algo útil, como um campo a ser ignorado / incumprimento / while desactivado se você estivesse usando unsigned você tem que reservar um valor especial como 2 ^ 32 -. 1 ou algo similar

Há muito tempo, quando a memória era limitada e processadores não automaticamente operar em 64 bits ao mesmo tempo, cada bit contado muito mais, por isso, ter assinado vs bytes não assinados ou shorts realmente importavam muito mais vezes e foi, obviamente, a decisão de design direito . Hoje apenas usando um int assinado é mais do que suficiente em quase todos os casos de programação regulares, e se o programa realmente precisa usar valores maiores que 2 ^ 31 - 1, muitas vezes você quer apenas um tempo de qualquer maneira. Quando estiver no território de usar anseia, é ainda mais difícil para chegar a uma razão pela qual você realmente não pode conviver com 2 ^ 63-1 inteiros positivos. Sempre que vamos para 128 processadores bit será ainda menos de um problema.

A sua pergunta é "Por que não suporta Java não assinados ints"?

E a minha resposta à sua pergunta é que Java quer que tudo isso de tipos primitivos: byte , caractere , curto , int e longo deve ser tratado como byte , palavra , dword e QWORD , respectivamente, exatamente como na montagem, e os operadores Java são assinados operações em todo o ele é tipos primitivos exceto para caractere , mas apenas em caractere são não assinado de 16 bits somente.

Assim, métodos estáticos supor para ser o não assinado operações de também para ambos os 32 e 64 bits.

Você precisa classe final, cujos métodos estáticos pode ser chamado para o não assinado operações.

Você pode criar essa classe final, chamar-lhe o nome que quiser e implementar seus métodos estáticos.

Se você não tem idéia sobre como implementar os métodos estáticos então este ligação pode ajudá-lo.

Na minha opinião, Java é não semelhante a C ++ em tudo , se ele nem suportam tipos não assinados nem sobrecarga de operadores, então eu acho que o Java deve ser tratado como idioma completamente diferente tanto C ++ e de C.

Ele também é completamente diferente em nome das línguas pelo caminho.

Então, eu não recomendo em Java para código de tipo semelhante ao C e eu não recomendo a digitar um código semelhante a C ++ em tudo, porque, em seguida, em Java você não será capaz de fazer o que você quer fazer em seguida em C ++, ou seja, o código não vai continuar a ser C ++ como em tudo e para mim isso é ruim para o código como esse, para alterar o estilo no meio.

Eu recomendo para escrever e usar métodos estáticos também para as operações assinadas, para que você não vê na mistura de código de operadores e métodos estáticos para ambas as operações assinados e não assinados, a menos que você só precisa de operações assinadas no código, e não há problema em usar somente os operadores.

Além disso, eu recomendo evitar o uso de curto , int e longo tipos primitivos, eo uso palavra , < strong> dword e QWORD operações, respectivamente, em vez disso, e você está prestes chamar os métodos estáticos para operações não assinados e / ou assinados em vez de usar operadores.

Se você está prestes a fazer operações assinadas e só usar os operadores apenas no código, então isso é bom para usar esses tipos primitivos curto , int e longo .

Na verdade palavra , dword e QWORD não não existe na língua, mas você pode criar nova classe para cada um e a implementação de cada um deve ser muito fácil:

A classe palavra contém o tipo primitivo curto única, a classe dword contém o tipo primitivo int única ea classe QWORD contém o tipo primitivo longo somente. Agora tudo o sem sinal e os métodos assinado como estática ou não como sua escolha, você pode implementar em cada classe, ou seja, todas as operações de 16 bits tanto não assinados e assinados por dar nomes que significam na palavra classe, todos as operações de 32 bits tanto não assinados e assinados por dar nomes que significam na dword classe e todas as operações de 64 bits tanto não assinados e assinados por dar nomes que significam na QWORD classe.

Se você não gosta dando muitos nomes diferentes para cada método, você sempre pode usar a sobrecarga em Java, bom ler que Java fez não remover essa também!

Se o que você quer métodos em vez de operadores de 8 bits assinadoPERAÇÕES e métodos para operações não assinados de 8 bits que não têm operadores em tudo, então você pode criar o Byte classe (note que a primeira letra 'B' é a capital, então este não é o tipo primitivo byte ) e implementar os métodos dessa classe.

Sobre a passagem por valor e passagem por referência:

Se não estou errado, como em C #, objetos primitivos são passados ??por valor, naturalmente, mas objetos de classe são passados ??por referência, naturalmente, o que significa que objetos do tipo Byte , palavra , dword e QWORD será passado por referência e não por valor, por padrão. Desejo Java teve struct objetos como C # tem, então tudo Byte , palavra , dword e QWORD pode ser implementado para ser struct em vez de class , então por padrão, eles foram passados ??por valor e não por referência, por padrão, como qualquer objeto struct em C #, como os tipos primitivos, são passados ??por valor e não por referência por padrão, mas porque que o Java é pior do que C # e temos de lidar com isso, então não há apenas as classes e interfaces, que são passados ??por referência e não por valor, por padrão. Então, se você quer passar Byte , palavra , dword e QWORD objetos de valor e não por referência, como qualquer outro objeto de classe em Java e também em C #, você terá que simplesmente usar o construtor de cópia e é isso.

Essa é a única solução que eu posso pensar. Eu só desejo que eu poderia apenas typedef os tipos primitivos a palavra, dword e QWORD, mas Java não typedef apoio nem usando em tudo, ao contrário de C # que suporta usando , o que equivale a typedef do C.

Sobre a saída:

Para o mesmo seqüência de bits , você pode imprimi-los de várias maneiras: como binário, como decimal (como o significado de% u em C printf), como octal (como o significado de% o em C printf), como hexadecimal (como o significado de X% em C printf) e de um número inteiro (como o significado de o d% em C printf).

Note que C printf não sabe o tipo das variáveis ??sendo passadas como parâmetros para a função, então printf sabe o tipo de cada variável apenas do char * objeto passado para o primeiro parâmetro da função.

Assim, em cada uma das classes: Byte , palavra , dword e QWORD , você pode implementar impressão método e obter a funcionalidade de printf, embora o tipo primitivo da classe é assinado, você ainda pode imprimi-lo como sem assinatura, seguindo um algoritmo envolvendo operações lógicas e de deslocamento para obter os dígitos para imprimir para a saída.

Infelizmente o link que lhe dei não mostra como implementar esses métodos de impressão, mas eu tenho certeza que você pode google para os algoritmos que você precisa para implementar esses métodos de impressão.

Isso é tudo que eu posso responder à sua pergunta e sugerir-lhe.

Como o tipo unsigned é pura maldade.

O fato de que em C unsigned - int produz unsigned é ainda mais mal.

Aqui está um instantâneo do problema que me queimou mais de uma vez:

// We have odd positive number of rays, 
// consecutive ones at angle delta from each other.
assert( rays.size() > 0 && rays.size() % 2 == 1 );

// Get a set of ray at delta angle between them.
for( size_t n = 0; n < rays.size(); ++n )
{
    // Compute the angle between nth ray and the middle one.
    // The index of the middle one is (rays.size() - 1) / 2,
    // the rays are evenly spaced at angle delta, therefore
    // the magnitude of the angle between nth ray and the 
    // middle one is: 
    double angle = delta * fabs( n - (rays.size() - 1) / 2 ); 

    // Do something else ...
}

Você já reparou o erro ainda? Confesso que só vi ele depois de pisar com o depurador.

Como n é de tipo sem sinal size_t todo o avalia expressão n - (rays.size() - 1) / 2 como unsigned. Essa expressão se destina a ser um assinado posição do raio nth daquele meio: o 1º ray daquele meio no lado esquerdo teria posição -1, a 1ª à direita teria posição +1, etc. Depois de tomar valor abs e multiplicando pelo ângulo delta gostaria de obter o ângulo entre ray nth e o do meio.

Infelizmente para mim a expressão acima continha o mal sem sinal e, em vez de avaliar a, digamos, -1, avaliou a 2 ^ 32-1. A conversão posterior para double selou o bug.

Depois de um bug ou dois causado por mau uso de unsigned uma aritmética tem que começar a se perguntar se a um bit extra fica vale a pena extra. Eu estou tentando, tanto quanto possível, para evitar qualquer uso de tipos unsigned em aritmética, embora ainda usá-lo para operações não-aritméticas tais como máscaras de binário.

Há algumas jóias na especificação 'C' que Java caiu por razões pragmáticas, mas que estão rastejando lentamente de volta com a demanda desenvolvedor (fechamentos, etc).

I mencionar um primeiro porque ele está relacionado a essa discussão; a aderência de valores de ponteiro para aritmético inteiro sem sinal. E, em relação a este tópico de discussão, a dificuldade de manter semântica não assinados no mundo Assinado de Java.

Eu acho que se fosse para obter um alter ego Dennis Ritchie para aconselhar de Gosling equipe de projeto que teria sugerido dando assinou um "zero no infinito", de modo que todos os pedidos de compensação de endereço seria primeiro adicionar seu tamanho ALGEBRAIC ANEL para negativo obviar valores.

Dessa forma, qualquer deslocamento jogado com a variedade nunca pode gerar um segfault. Por exemplo, em uma classe encapsulado que eu chamo RingArray de duplas que precisa de comportamento não assinado - em "auto rotação laço" contexto:

// ...
// Housekeeping state variable
long entrycount;     // A sequence number
int cycle;           // Number of loops cycled
int size;            // Active size of the array because size<modulus during cycle 0
int modulus;         // Maximal size of the array

// Ring state variables
private int head;   // The 'head' of the Ring
private int tail;   // The ring iterator 'cursor'
// tail may get the current cursor position
// and head gets the old tail value
// there are other semantic variations possible

// The Array state variable
double [] darray;    // The array of doubles

// somewhere in constructor
public RingArray(int modulus) {
    super();
    this.modulus = modulus;
    tail =  head =  cycle = 0;
    darray = new double[modulus];
// ...
}
// ...
double getElementAt(int offset){
    return darray[(tail+modulus+offset%modulus)%modulus];
}
//  remember, the above is treating steady-state where size==modulus
// ...

O acima RingArray que nunca 'get' a partir de um índice negativo, mesmo se um solicitante malicioso tentou. Lembre-se, há também muitas solicitações legítimas para pedir anteriores (negativos) valores de índice.

NB: O% exterior pedidos módulo de referências legítimos, enquanto que as máscaras% módulo interno para fora maldade evidente a partir dos negativos mais negativo do que -modulus. Se isto fosse sempre aparecem em uma Java + .. + 9 || 8 + .. + spec, então o problema seria realmente tornar-se um 'programador que não pode 'auto girar' FAULT'.

Eu tenho certeza que o chamado Java não assinado int 'deficiência' podem ser feitas até para com o acima de uma linha.

PS: Apenas para dar contexto acima RingArray limpeza, aqui está um candidato 'set' operação para coincidir com a 'get' operação do elemento acima:

void addElement(long entrycount,double value){ // to be called only by the keeper of entrycount
    this.entrycount= entrycount;
    cycle = (int)entrycount/modulus;
    if(cycle==0){                       // start-up is when the ring is being populated the first time around
        size = (int)entrycount;         // during start-up, size is less than modulus so use modulo size arithmetic
        tail = (int)entrycount%size;    //  during start-up
    }
    else {
        size = modulus;
        head = tail;
        tail = (int)entrycount%modulus; //  after start-up
    }
    darray[head] = value;               //  always overwrite old tail
}

Não consigo pensar em um infeliz efeito colateral. Nos bancos de dados Java embutido, o número de ids que você pode ter com um campo de 32 bits ID é 2 ^ 31, não 2 ^ 32 (~ 2 bilhões, não ~ 4 bilhões).

O IMHO razão é porque eles são / foram muito preguiçoso para implementar / corrigir esse erro. Sugerindo que os programadores C / C ++ não entende sem sinal, estrutura, união, bandeira pouco ... É simplesmente absurdo.

Ether você estava falando com um programador básico / bash / java na iminência de iniciar a programação de um la C, sem qualquer conhecimento real esta língua ou você está apenas falando para fora de sua própria mente. ;)

quando você lida todos os dias em formato quer a partir de arquivo ou hardware que você começa a pergunta, o que diabos eles estavam pensando.

Um bom exemplo aqui seria tentar usar um byte não assinado como um ciclo de rotação auto. Para aqueles de vocês que não entendem a última frase, como na terra você chamar-se um programador.

DC

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top