Por que o Java não inclui suporte para números inteiros não assinados?
Parece-me uma omissão estranha, uma vez que eles permitem escrever código com menor probabilidade de produzir estouros em entradas inesperadamente grandes.
Além disso, o uso de números inteiros não assinados pode ser uma forma de autodocumentação, pois indica que o valor que o int não assinado deveria conter nunca deve ser negativo.
Por fim, em alguns casos, números inteiros não assinados podem ser mais eficientes para determinadas operações, como divisão.
Qual é a desvantagem de incluí-los?
java
language-design
unsigned
integer
dsimcha
fonte
fonte
byte
não conseguem dar um140
nível de cinza direto , mas-116
é necessário& 0xff
obter o valor correto.Respostas:
Isto é de uma entrevista com Gosling e outros , sobre simplicidade:
fonte
Lendo nas entrelinhas, acho que a lógica era mais ou menos assim:
Principalmente, eu diria que foi uma decisão razoável. Possivelmente, eu teria:
Ainda assim, com algumas críticas, as operações com valores não assinados de até 32 bits não são muito ruins e a maioria das pessoas não precisa de divisão ou comparação de 64 bits não assinada.
fonte
short
é usado - os algoritmos defltate / gzip / inflate são de 16 bits e dependem muito de shorts ... ou pelo menosshort[]
[reconhecidamente eles são nativos - ainda que o Java impl do algoritmo carregue terrabytes de dados]. O último (short[]
) tem uma vantagem significativa,int[]
pois leva duas vezes menos memória e menos memória = melhores propriedades de armazenamento em cache, desempenho muito melhor.Esta é uma pergunta mais antiga e pat mencionou brevemente char, eu apenas pensei em expandir isso para outras pessoas que olharão para isso no futuro. Vamos dar uma olhada nos tipos primitivos do Java:
byte
- inteiro assinado de 8 bitsshort
- inteiro assinado de 16 bitsint
- inteiro assinado de 32 bitslong
- inteiro assinado de 64 bitschar
- caractere de 16 bits (número inteiro não assinado)Embora
char
não suporteunsigned
aritmética, essencialmente pode ser tratado como umunsigned
número inteiro. Você precisaria converter explicitamente operações aritméticaschar
, mas isso fornece uma maneira de especificarunsigned
números.Sim, não há suporte direto para números inteiros não assinados (obviamente, eu não precisaria converter a maioria das minhas operações em char se houvesse suporte direto). No entanto, certamente existe um tipo de dados primitivo não assinado. Eu gostaria de ter visto um byte não assinado também, mas acho que dobrar o custo da memória e usar char é uma opção viável.
Editar
Com o JDK8, há novas APIs para
Long
eInteger
que fornecem métodos auxiliares ao tratarlong
eint
valores como valores não assinados.compareUnsigned
divideUnsigned
parseUnsignedInt
parseUnsignedLong
remainderUnsigned
toUnsignedLong
toUnsignedString
Além disso, o Guava fornece vários métodos auxiliares para realizar ações semelhantes nos tipos de números inteiros, o que ajuda a fechar a lacuna deixada pela falta de suporte nativo para
unsigned
números inteiros.fonte
char
é muito pequeno para suportarlong
aritmética, por exemplo.Java tem tipos não assinados, ou pelo menos um: char é um curto não assinado. Então, qualquer que seja a desculpa que Gosling vomite, é realmente apenas sua ignorância o motivo de não haver outros tipos não assinados.
Também tipos curtos: os shorts são usados o tempo todo para multimídia. O motivo é que você pode ajustar 2 amostras em um único comprimento não assinado de 32 bits e vetorizar muitas operações. A mesma coisa com dados de 8 bits e byte não assinado. Você pode colocar 4 ou 8 amostras em um registro para vetorizar.
fonte
char
exceto para personagens.Assim que assinado e ints não assinados são misturados em uma expressão coisas começam a ficar confuso e você provavelmente irá perder informações. Restringir o Java a entradas assinadas apenas realmente esclarece as coisas. Fico feliz por não ter que me preocupar com toda a empresa assinada / não assinada, embora às vezes perca o oitavo bit em um byte.
fonte
static_cast
muito s para misturá-los. É realmente confuso.byte
sido assinado como em Pascal.& 0xFF
todas as promoções de byte para int tornam o código ainda mais confuso.http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html
Esse cara diz que porque o padrão C define operações que envolvem entradas não assinadas e assinadas para serem tratadas como não assinadas. Isso pode fazer com que números inteiros negativos assinados rolem para um int não assinado grande, potencialmente causando bugs.
fonte
-1
- com qualquer quantidade não assinada - mesmo zero.-1
idade "desconhecida" (como o artigo sugere) é um dos exemplos clássicos de "cheiro de código" . Por exemplo, se você deseja calcular "quanto Alice é mais velha que Bob?" E A = 25 e B = -1, você receberá uma resposta±26
que está simplesmente errada. O tratamento adequado de valores desconhecidos é algum tipo deOption<TArg>
quandoSome(25) - None
retornariaNone
.Eu acho que Java está bem, como adicionar não assinado complicaria sem muito ganho. Mesmo com o modelo inteiro simplificado, a maioria dos programadores Java não sabe como os tipos numéricos básicos se comportam - basta ler o livro Java Puzzlers para ver quais conceitos errôneos você pode ter.
Quanto aos conselhos práticos:
Se seus valores tiverem tamanho arbitrário e não se ajustarem
int
, uselong
. Se eles não se encaixam nolong
usoBigInteger
.Use os tipos menores apenas para matrizes quando precisar economizar espaço.
Se você precisar exatamente de 64/32/16/8 bits, use
long
/int
/short
/byte
e pare de se preocupar com o bit de sinal, exceto divisão, comparação, deslocamento à direita e conversão.Consulte também esta resposta sobre "portar um gerador de números aleatórios de C para Java".
fonte
>>
e>>>
para assinado e não assinado, respectivamente. Deslocar para a esquerda não é problema.>>>
não funciona parashort
ebyte
. Por exemplo,(byte)0xff>>>1
produz em0x7fffffff
vez de0x7f
. Outro exemplo:byte b=(byte)0xff; b>>>=1;
resultará emb==(byte)0xff
. Claro que você pode fazer,b=(byte)(b & 0xff >> 1);
mas isso adiciona mais uma operação (bit a bit &).Com o JDK8, ele tem algum suporte para eles.
Ainda podemos ver o suporte total de tipos não assinados em Java, apesar das preocupações de Gosling.
fonte
Eu sei que este post é muito antigo; no entanto, para o seu interesse, em Java 8 e posterior, você pode usar o
int
tipo de dados 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 aInteger
classe para usar oint
tipo de dados como um número inteiro não assinado e métodos estáticos comocompareUnsigned()
,divideUnsigned()
etc., foram adicionados àInteger
classe para suportar as operações aritméticas de números inteiros não assinados.fonte
Ouvi histórias de que elas deveriam ser incluídas perto da versão Java original. Oak foi o precursor do Java e, em alguns documentos de especificação, houve menção de valores designados. Infelizmente, eles nunca chegaram à linguagem Java. Tanto quanto alguém conseguiu descobrir, eles simplesmente não foram implementados, provavelmente devido a uma restrição de tempo.
fonte
char
) foram deixados de fora porque os designers pensaram que eram uma má idéia ... dados os objetivos da linguagem.Certa vez, fiz um curso de C ++ com alguém no comitê de padrões de C ++ que sugeriu que o Java tomou a decisão correta de evitar números inteiros não assinados porque (1) a maioria dos programas que usam números inteiros não assinados pode fazer o mesmo com números inteiros assinados e isso é mais natural em termos de como as pessoas pensam e (2) usar números inteiros não assinados resultam em muitos problemas fáceis de criar, mas difíceis de depurar, como excesso aritmético de números inteiros e perda de bits significativos ao converter entre tipos assinados e não assinados. Se você subtrair 1 de 0 por engano usando números inteiros assinados, muitas vezes causará mais falhas no programa e facilitará a localização do bug do que se ele se aproximar de 2 ^ 32 - 1, e os compiladores e ferramentas de análise estática e as verificações de tempo de execução precisam suponha que você saiba o que está fazendo desde que escolheu usar aritmética não assinada. Além disso,
Há muito tempo, quando a memória era limitada e os processadores não operavam automaticamente em 64 bits de uma só vez, cada bit contava muito mais; portanto, ter bytes ou shorts com sinal versus sem sinal realmente importava com muito mais frequência e era obviamente a decisão de design correta. Hoje, apenas o uso de um int assinado é mais do que suficiente em quase todos os casos de programação regulares, e se o seu programa realmente precisar usar valores maiores que 2 ^ 31 - 1, muitas vezes você só quer um longo. Quando você está no território de usar longs, é ainda mais difícil encontrar uma razão pela qual você realmente não consegue conviver com 2 ^ 63 - 1 números inteiros positivos. Sempre que formos para processadores de 128 bits, será um problema ainda menor.
fonte
Sua pergunta é "Por que o Java não suporta entradas não assinadas"?
E minha resposta para sua pergunta é que o Java quer que todos os seus tipos primitivos: byte , char , short , int e long sejam tratados como byte , word , dword e qword respectivamente, exatamente como no assembly, e os operadores Java sejam assinados operações em todos os seus tipos primitivos, exceto char , mas somente em char eles não têm assinatura de 16 bits.
Portanto, os métodos estáticos supõem ser as operações não assinadas também para 32 e 64 bits.
Você precisa da classe final, cujos métodos estáticos podem ser chamados para o não assinado operações .
Você pode criar essa classe final, chamá-la como quiser e implementar seus métodos estáticos.
Se você não tem idéia de como implementar os métodos estáticos, este link pode ajudá-lo.
Na minha opinião, Java é não semelhante a C ++ em tudo , se ele não suportar 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.
A propósito, também é completamente diferente no nome dos idiomas.
Portanto, não recomendo em Java digitar código semelhante ao C e não recomendo digitar código semelhante ao C ++, porque, em Java, você não poderá fazer o que deseja fazer em C ++, ou seja, o código não continuará sendo como o C ++ e, para mim, é ruim codificar dessa maneira, mudar o estilo no meio.
Eu recomendo escrever e usar métodos estáticos também para as operações assinadas, para que você não veja na mistura de códigos operadores e métodos estáticos para operações assinadas e não assinadas, a menos que você precise apenas de operações assinadas no código e não há problema em use apenas os operadores.
Também recomendo evitar o uso de tipos primitivos curtos , int e longos e , em vez disso, use word , dword e qword respectivamente, e você deve chamar os métodos estáticos para operações não assinadas e / ou operações assinadas, em vez de usar operadores.
Se você estiver prestes a executar apenas operações assinadas e usar os operadores apenas no código, não há problema em usar esses tipos primitivos short , int e long .
Na verdade palavra , dword e QWORD que 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 palavra da classe mantém apenas o tipo primitivo curto , a classe dword mantém o tipo primitivo int apenas e a classe qword mantém apenas o tipo primitivo por muito tempo . Agora, todos os métodos não assinados e assinados são estáticos ou não, conforme sua escolha, você pode implementar em cada classe, ou seja, todas as operações de 16 bits não assinadas e assinadas, fornecendo nomes de significado na palavra classe, todas as operações de 32 bits não assinadas e assinado dando nomes de significado na classe dword e todas as operações de 64 bits não assinadas e assinadas dando nomes de significado na classe qword .
Se você não gosta de dar muitos nomes diferentes para cada método, sempre pode usar sobrecarga em Java, é bom ler que o Java não removeu isso também!
Se você deseja métodos, em vez de operadores para operações assinadas de 8 bits e métodos para operações não assinadas de 8 bits que não possuem operadores, crie a classe Byte (observe que a primeira letra 'B' é maiúscula, portanto essa não é a byte do tipo primitivo ) e implemente os métodos nesta classe.
Sobre passar por valor e passar 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 , word , dword e qword serão passados por referência e não por valor por padrão. Eu gostaria que o Java tivesse objetos struct como o C #, para que todos os bytes , palavras , dword e qword pudessem ser implementados para serem struct em vez de classe, portanto, 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 esse Java é pior que C # e temos Para lidar com isso, existem apenas classes e interfaces que são passadas por referência e não por valor por padrão. Portanto, se você deseja passar os objetos Byte , word , dword e qword por valor e não por referência, como qualquer outro objeto de classe em Java e também em C #, terá que simplesmente usar o construtor de cópia e pronto .
Essa é a única solução em que consigo pensar. Eu gostaria de poder apenas digitar os tipos primitivos em word, dword e qword, mas o Java não suporta typedef nem usa nada, ao contrário do C # que suporta o uso , que é equivalente ao typedef do C.
Sobre a saída:
Para a mesma sequência de bits , você pode imprimi-los de várias maneiras: como binário, 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 como número inteiro (como o significado de% d em C printf).
Observe que C printf não sabe o tipo das variáveis que estão sendo passadas como parâmetros para a função, portanto printf conhece o tipo de cada variável apenas do objeto char * passado para o primeiro parâmetro da função.
Portanto, em cada uma das classes: Byte , word , dword e qword , é possível implementar o método print e obter a funcionalidade do printf, mesmo que o tipo primitivo da classe seja assinado, você ainda pode imprimi-lo como não assinado, seguindo algum algoritmo envolvendo operações lógicas e de deslocamento para obter os dígitos a serem impressos na saída.
Infelizmente, o link que forneci não mostra como implementar esses métodos de impressão, mas tenho certeza de que você pode pesquisar no Google os algoritmos necessários para implementar esses métodos de impressão.
É tudo o que posso responder à sua pergunta e sugerir você.
fonte
Porque o
unsigned
tipo é puro mal.O fato de que em C
unsigned - int
produzunsigned
é ainda mais mau.Aqui está um instantâneo do problema que me queimou mais de uma vez:
Você já reparou no bug? Confesso que só vi depois de entrar com o depurador.
Como
n
é do tipo não assinado,size_t
a expressão inteira én - (rays.size() - 1) / 2
avaliada comounsigned
. Essa expressão pretende ser uma posição assinada don
raio th a partir do meio: o primeiro raio do meio no lado esquerdo teria a posição -1, o primeiro na direita teria a posição +1, etc. tomando o valor abs e multiplicando pelodelta
ângulo, obteria o ângulo entre on
raio e o meio.Infelizmente para mim, a expressão acima continha o mal não assinado e, em vez de avaliar para, digamos, -1, avaliou para 2 ^ 32-1. A conversão subsequente para
double
selou o bug.Depois de um bug ou dois causado pelo uso incorreto da
unsigned
aritmética, é preciso começar a se perguntar se o bit extra obtido vale a pena. Estou tentando, tanto quanto possível, evitar qualquer uso deunsigned
tipos em aritmética, embora ainda o utilize para operações não aritméticas, como máscaras binárias.fonte
unsigned
é convertidoint
em todas as operações, para que serveunsigned
? Não terá nenhuma funcionalidade distinguívelshort
. E se você converterint
apenas para operações mistas, comounsigned+int
ouunsigned+float
, ainda terá o problema de((unsigned)25-(unsigned)30)*1.0 > 0
, que é uma das principais causas deunsigned
erros relacionados.exit(1);
realmente "vale a pena extra"? Ser capaz de abrir arquivos grandes realmente vale a segurança que os programadores java menos experientes não estragamunsigned
?n - (rays.size() - 1) / 2
. Você deve sempre agrupar operadores binários, porque o leitor do código não precisa assumir nada sobre a ordem das operações em um programa de computador. Só porque dizemos convencionalmente que a + b c = a + (b c) não significa que você possa assumir isso ao ler código. Além disso, o cálculo deve ser definido fora do loop, para que possa ser testado sem o loop presente. Este é um erro ao não garantir que seus tipos estejam alinhados, em vez de um problema de números inteiros não assinados. Em C, cabe a você garantir que seus tipos estejam alinhados.Existem algumas gemas na especificação 'C' que o Java caiu por motivos pragmáticos, mas que estão lentamente voltando atrás com a demanda dos desenvolvedores (fechamentos, etc).
Menciono um primeiro porque está relacionado a esta discussão; a aderência dos valores do ponteiro à aritmética de número inteiro não assinado. E, em relação a este tópico do encadeamento, a dificuldade de manter a semântica não assinada no mundo assinado de Java.
Eu acho que se alguém tivesse um alter ego de Dennis Ritchie para aconselhar a equipe de design de Gosling, sugeriria que o Signed tivesse um "zero no infinito", para que todas as solicitações de deslocamento de endereço adicionassem primeiro seu TAMANHO DE ANEL ALGEBRAICO para evitar valores negativos.
Dessa forma, qualquer deslocamento lançado na matriz nunca pode gerar um SEGFAULT. Por exemplo, em uma classe encapsulada que eu chamo de RingArray de duplas que precisa de um comportamento não assinado - no contexto de "loop rotativo automático":
O RingArray acima nunca 'obteria' de um índice negativo, mesmo que um solicitante malicioso tentasse. Lembre-se, também há muitos pedidos legítimos para solicitar valores de índice anteriores (negativos).
NB: O módulo% externo de-referencia solicitações legítimas, enquanto o módulo% interno mascara a malícia flagrante de negativos mais negativos do que o módulo. Se isso alguma vez aparecer em um Java + .. + 9 || 8 + .. + spec, então o problema se tornaria genuinamente um 'programador que não pode "auto-girar" a FALHA ".
Tenho certeza de que a chamada 'deficiência não assinada' de Java pode ser compensada com o one-liner acima.
PS: Apenas para contextualizar a limpeza do RingArray acima, aqui está uma operação 'set' candidata para corresponder à operação do elemento 'get' acima:
fonte
Eu posso pensar em um efeito colateral infeliz. Nos bancos de dados Java incorporados, o número de IDs que você pode ter com um campo de 32 bits é 2 ^ 31, não 2 ^ 32 (~ 2 bilhões, não ~ 4 bilhões).
fonte
A razão pela qual o IMHO é porque eles são / eram preguiçosos demais para implementar / corrigir esse erro. Sugerir que os programadores de C / C ++ não entendam não assinado, estrutura, união, sinalizador de bits ... É apenas absurdo.
Éter, você estava conversando com um programador básico / bash / java a ponto de começar a programar a la C, sem nenhum conhecimento real dessa linguagem ou você está apenas falando mal da sua mente. ;)
quando você lida todos os dias em formato de arquivo ou hardware, começa a questionar o que diabos eles estavam pensando.
Um bom exemplo aqui seria tentar usar um byte não assinado como um loop rotativo. Para aqueles que não entendem a última frase, como você se considera um programador?
DC
fonte