Por que o Java não suporta entradas não assinadas?


374

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?


137
Eu não sei, mas isso me irrita muito; por exemplo, é muito mais difícil escrever código de rede dessa maneira.
Tamas Czinege 10/01/09

20
Eu gostaria que houvesse apenas dois tipos na língua / database / ... mundo: número e string de :)
Liao

5
Escrever código de rede não é muito mais difícil. BTW InputStream.read (), retorna um byte não assinado, não um assinado, por exemplo, para que o exemplo de rede seja um IMHO de confusão. Sua única confusão é que você supõe que escrever um valor assinado seja diferente de escrever um valor não assinado. ou seja, se você realmente não sabe o que está acontecendo no nível de bytes.
Peter Lawrey 27/08/10

19
@ZachSaw - Também fiz uma análise dupla quando vi um designer de linguagem fazer essa citação. Não há nada mais simples que um número inteiro não assinado. Inteiros assinados são complicados. Particularmente quando você considera o bit girando no nível do transistor. E como um número inteiro assinado muda? Eu tive que concluir que o designer de Java tem um problema sério de compreensão da lógica booleana.
PP.

8
Para mim, fica mais difícil fazer qualquer processamento de imagem, pois as imagens bytenão conseguem dar um 140nível de cinza direto , mas -116é necessário & 0xffobter o valor correto.
Matthieu

Respostas:


193

Isto é de uma entrevista com Gosling e outros , sobre simplicidade:

Gosling: Para mim, como designer de linguagem, o que realmente não me considero hoje em dia, o que "simples" realmente significava é que eu poderia esperar que o J. Random Developer segure a especificação em sua cabeça. Essa definição diz que, por exemplo, Java não é - e, de fato, muitas dessas linguagens acabam com muitos casos extremos, coisas que ninguém realmente entende. Teste qualquer desenvolvedor C sobre não assinado e logo você descobre que quase nenhum desenvolvedor C realmente entende o que acontece com o não assinado, o que é a aritmética não assinada. Coisas assim tornavam C complexo. A parte da linguagem do Java é, eu acho, bastante simples. As bibliotecas que você precisa procurar.


222
Vou ter que discordar de Gosling aqui com um exemplo específico (da CLR, não menos). O que é mais confuso fornecer a uma matriz um valor de comprimento inteiro assinado ou um comprimento não assinado? É impossível para uma matriz ter um comprimento negativo, mas nossa API indica que isso é possível.
JaredPar 10/01/09

18
O argumento de tornar o Java simples é parte do que nos levou à confusão, com a falta de modelos que eles trouxeram para a linguagem porque as alternativas eram muito complicadas. Eu acho que se poderia apoiar ints não assinados com uma classe apropriada, porém, ele não precisa de prims
Uri

59
Se o Java precisar de números inteiros não assinados porque os índices da matriz não podem ser negativos, ele também precisará de subintervalos (à la Pascal) porque um índice da matriz não pode ser maior que o tamanho da matriz.
Wayne Conrad

81
Ok, ele apenas disse as vantagens de não ter tipos não assinados. Agora vamos contar as desvantagens ...
Moshe Revah

83
Eu prefiro a simplicidade do código do que a simplicidade da linguagem. É por isso que eu odeio Java.
Pijusn 16/09/12

50

Lendo nas entrelinhas, acho que a lógica era mais ou menos assim:

  • geralmente, os designers de Java queriam simplificar o repertório de tipos de dados disponíveis
  • para fins cotidianos, eles achavam que a necessidade mais comum era de tipos de dados assinados
  • para implementar certos algoritmos, a aritmética não assinada às vezes é necessária, mas o tipo de programador que implementaria esses algoritmos também teria o conhecimento de "contornar" a aritmética não assinada com tipos de dados assinados

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

  • byte não assinado ou, pelo menos, forneceu alternativas assinadas / não assinadas, possivelmente com nomes diferentes, para este tipo de dados (torná-lo assinado é bom para consistência, mas quando você precisa de um byte assinado?)
  • eliminado o 'short' (quando você usou a aritmética com sinal de 16 bits pela última vez?)

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.


2
Também adoraria ter bytes não assinados, mas suspeito que a vantagem da consistência completa entre os tipos inteiros supera a conveniência que os bytes não assinados trariam.
Alan Moore

64
"Para fins cotidianos, eles sentiram que a necessidade mais comum era de tipos de dados assinados". No meu código C ++, quase sempre me pego pensando "Por que diabos estou usando um número inteiro assinado aqui em vez de um número inteiro não assinado ?!". Eu tenho a sensação de que "assinado" é a exceção e não a regra (é claro, depende do domínio, mas há uma razão pela qual inteiros positivos são chamados números naturais ;-)).
Luc Touraille

15
thumb up thumb down [...] chamada de bytes não assinados, ao fazer o processamento de imagens, assumindo que os bytes não sejam assinados (como deveria ser), me fez passar horas depurando.
Helin Wang

7
você surpreendeu com que frequência shorté usado - os algoritmos defltate / gzip / inflate são de 16 bits e dependem muito de shorts ... ou pelo menos short[][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.
bestsss

8
Embora em um aplicativo específico, você deve avaliar se o uso de shorts oferece melhor desempenho, em vez de assumir que é verdade. É possível que o trabalho extra de manobra extra necessário para manipular shorts em vez de ints (que geralmente é do tipo que o processador 'gosta de usar') possa realmente prejudicar o desempenho em um aplicativo específico. Nem sempre, mas você deve testar, não assumir.
Neil Coffey

19

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 bits

short - inteiro assinado de 16 bits

int - inteiro assinado de 32 bits

long - inteiro assinado de 64 bits

char - caractere de 16 bits (número inteiro não assinado)

Embora charnão suporte unsignedaritmética, essencialmente pode ser tratado como um unsignednúmero inteiro. Você precisaria converter explicitamente operações aritméticas char, mas isso fornece uma maneira de especificar unsignednúmeros.

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 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 Longe Integerque fornecem métodos auxiliares ao tratar longe intvalores 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 unsignednúmeros inteiros.


2
Mas, no entanto, charé muito pequeno para suportar longaritmética, por exemplo.

3
Isso pode ser uma desvantagem do Java

Esperando que eles suportem valores não assinados para bytes. Torna as coisas mais fáceis.
Mixz #

15

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.


37
Sim, eu tenho certeza que Gosling é muito ignorante sobre Java em comparação com você.
jakeboxer

O Java permite que a aritmética seja executada diretamente em quantidades de bytes não assinados ou os valores sempre são promovidos? Ter um tipo não assinado para armazenamento, mas sempre executando aritmética em um tipo assinado, grande o suficiente para acomodá-lo, funciona bem semanticamente, mas faria com que as operações em tipos não assinados, do mesmo tamanho dos números inteiros "normais", fossem mais caras.
Supercat

2
É um estilo ruim de usar, charexceto para personagens.
estrela azul

5
@starblue Claro que sim, mas é um truque para contornar uma limitação do idioma
Básico

14

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.


12
Quanto à mixagem assinada / não assinada: você pode ter tipos não assinados, mas não permitir a mixagem (ou exigir conversões explícitas). Ainda assim, não está claro se é necessário.
sleske

2
Em C ++, você precisa espalhar static_castmuito s para misturá-los. É realmente confuso.
Raedwald 2/08

4
O oitavo bit está lá, ele apenas tenta se esconder como o sinal.
starblue

As coisas só ficam confusas com tipos de 32 bits ou maiores. Não vejo razão para o Java não ter bytesido assinado como em Pascal.
Supercat

12
Venha me ver quando tiver problemas com o processamento de imagens em Java, onde espera que os bytes não sejam assinados. Então você saberá que & 0xFFtodas as promoções de byte para int tornam o código ainda mais confuso.
bit2shift #

12

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.


34
Inteiros assinados em Java também circulam. Eu não entendo o seu ponto.
foo

8
@ fu: números inteiros assinados têm que ficar grandes antes que causem problemas. Por outro lado, em C, pode-se ter problemas ao comparar qualquer número inteiro negativo - mesmo -1- com qualquer quantidade não assinada - mesmo zero.
Supercat

É uma pena que o Java não possa ter incluído tipos não assinados, mas com um conjunto limitado de conversões e operadores mistos (algo análogo ao modo como em C se pode adicionar 5 a um ponteiro, mas não se pode comparar um ponteiro a 5) . A ideia de que o uso de um operador em tipos mistos quando existe uma conversão implícita deve forçar o uso implícito dessa conversão (e usar o tipo conseqüente como o tipo de resultado) está no centro de muitas decisões de design duvidosas no .NET e no .NET. Java.
Supercat 03/09

4
Não se queixar da sua resposta, mas ter uma -1idade "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 ±26que está simplesmente errada. O tratamento adequado de valores desconhecidos é algum tipo de Option<TArg>quando Some(25) - Noneretornaria None.
bytebuster

11

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, use long. Se eles não se encaixam no longuso BigInteger.

  • 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/ bytee 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".


5
Sim, para mudar para a direita, você deve escolher entre >>e >>>para assinado e não assinado, respectivamente. Deslocar para a esquerda não é problema.
starblue

11
@starblue Na verdade >>>não funciona para shorte byte. Por exemplo, (byte)0xff>>>1produz em 0x7fffffffvez de 0x7f. Outro exemplo: byte b=(byte)0xff; b>>>=1;resultará em b==(byte)0xff. Claro que você pode fazer, b=(byte)(b & 0xff >> 1);mas isso adiciona mais uma operação (bit a bit &).
CITBL

7
"... Mesmo com o modelo simplificado, a maioria dos programadores Java não sabe como os tipos numéricos básicos se comportam ..." Algo em mim apenas se ressente de uma linguagem voltada para o menor denominador comum.
Básico

A linha de abertura na sua resposta, sobre mais complicações e pouco ganho, é precisamente o que elaborei
Nayuki

11
@Nayuki Seu artigo é muito bom. Apenas uma pequena observação, eu usaria a adição de 0x80000000 para operadores de comparação em vez de XOR, porque explica por que funciona, muda a região contígua em que a comparação ocorre de -MAXINT para 0. Bitwise, seu efeito é exatamente o mesmo.
Starblue

6

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.


12
aka "Então, as pessoas realmente o usam e estávamos errados em não incluí-lo no início" - mas ainda não confiamos que os desenvolvedores Java saibam se uma variável está assinada ou não - então não vamos implementá-los na VM ou como tipos equivalentes aos primos assinados.
Básico

6

Eu sei que este post é muito antigo; no entanto, para o seu interesse, em Java 8 e posterior, você pode usar o inttipo 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 a Integerclasse para usar o inttipo de dados como um número inteiro não assinado e métodos estáticos como compareUnsigned(), divideUnsigned()etc., foram adicionados à Integerclasse para suportar as operações aritméticas de números inteiros não assinados.


4

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.


Isso seria bom ... exceto que as evidências da entrevista de Gosling implicam que números inteiros não assinados (exceto char) foram deixados de fora porque os designers pensaram que eram uma má idéia ... dados os objetivos da linguagem.
Stephen C

É uma boa idéia nunca colocar muito valor nas declarações das testemunhas oculares, se a evidência documental também estiver disponível.
user7610

4

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.


2

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ê.


MASM (assembler da Microsoft) e Windows definem BYTE, WORD, DWORD, QWORD, como tipos não assinados. Para MASM, SBYTE, SWORD, SDWORD, SQWORD são os tipos assinados.
Rcgldr 22/04/19

1

Porque o unsignedtipo é puro mal.

O fato de que em C unsigned - intproduz unsignedé ainda mais mau.

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 no bug? Confesso que só vi depois de entrar com o depurador.

Como né do tipo não assinado, size_ta expressão inteira é n - (rays.size() - 1) / 2avaliada como unsigned. Essa expressão pretende ser uma posição assinada do nraio 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 pelo deltaângulo, obteria o ângulo entre o nraio 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 doubleselou o bug.

Depois de um bug ou dois causado pelo uso incorreto da unsignedaritmética, é preciso começar a se perguntar se o bit extra obtido vale a pena. Estou tentando, tanto quanto possível, evitar qualquer uso de unsignedtipos em aritmética, embora ainda o utilize para operações não aritméticas, como máscaras binárias.


Adicionar "não assinado por muito tempo" ao Java seria estranho. A adição de tipos menores não assinados, no entanto, não deveria ter problema. Especialmente tipos menores que "int" poderiam ter sido facilmente manipulados, promovendo-os a "int" de maneira numericamente óbvia, e "unsigned int" poderia ter sido tratado dizendo que operações envolvendo um int assinado e um int não assinado promoverão ambos os operandos para "long". A única situação problemática seria operações envolvendo uma quantidade longa não assinada e uma quantidade assinada, uma vez que não haveria um tipo capaz de representar todos os valores de ambos os operandos.
supercat

@ supercat: se unsignedé convertido intem todas as operações, para que serve unsigned? Não terá nenhuma funcionalidade distinguível short. E se você converter intapenas para operações mistas, como unsigned+intou unsigned+float, ainda terá o problema de ((unsigned)25-(unsigned)30)*1.0 > 0, que é uma das principais causas de unsignederros relacionados.
Michael

Muitas operações em tipos não assinados seriam promovidas para "longas". Exigir conversões explícitas ao armazenar o resultado de volta para tipos não assinados causaria os mesmos aborrecimentos que existem com short e byte, mas se o tipo for principalmente um formato de armazenamento em vez de um formato de computação que não deve ser um problema. Em qualquer caso, os tipos não assinados menores que "int" devem simplesmente ser capazes de promover para "int" sem dificuldade.
Supercat 26/09

3
Não gosto desta resposta porque usa o argumento "números inteiros não assinados são maus e não deveriam existir porque nunca podem ser assinados". Qualquer pessoa que tente subtrair um número inteiro não assinado já deve saber disso. E quanto à legibilidade, C não é exatamente conhecido por ser fácil de seguir. Além disso, o argumento (semi-) "o bit extra não vale a pena o problema extra" também é muito fraco. O tratamento de erros em vez de 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 estragam unsigned?
yyny

2
A única coisa má que vejo neste código é 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.
Dmitry

0

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":

// ...
// 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 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:

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
}

-2

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).


11
Ele provavelmente está pensando em matrizes e não pode usar números inteiros negativos como índices. Provavelmente.
SK9 19/01/19

2
Quando os campos de incremento automático nos bancos de dados transbordam, eles geralmente ficam loucos.
Josué

-8

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


34
Apenas para brincadeiras, procure no Google a frase "auto-rotating loop". Claramente , Denis Co é a única pessoa no mundo digno de chamar-se / ela mesma um programador :-)
Stephen C

6
Esta resposta é tão ruim que é engraçado
Nayuki
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.