Почему Java не поддерживает неподписанные целые числа?

StackOverflow https://stackoverflow.com/questions/430346

Вопрос

Почему Java не включает поддержку целых чисел без знака?

Мне кажется, это странное упущение, учитывая, что они позволяют писать код, который с меньшей вероятностью приведет к переполнению при неожиданно больших входных данных.

Кроме того, использование целых чисел без знака может быть формой самодокументации, поскольку они указывают, что значение, которое должен был содержать unsigned int, никогда не должно быть отрицательным.

Наконец, в некоторых случаях целые числа без знака могут быть более эффективными для определенных операций, таких как деление.

В чем недостаток их включения?

Это было полезно?

Решение

Это из интервью с Гослингом и другими , о простоте:

  

Гослинг: Для меня как для дизайнера языков, который я не считаю себя таким, как сегодня, что "просто" на самом деле это означало, мог ли я ожидать, что J. Random Developer будет держать спецификацию в своей голове. Это определение говорит о том, что, например, Java не является - и на самом деле многие из этих языков заканчиваются множеством угловых случаев, вещей, которые на самом деле никто не понимает. Опросите любого разработчика на C о unsigned, и довольно скоро вы обнаружите, что почти никто из разработчиков C не понимает, что происходит с unsigned, что такое беззнаковая арифметика. Такие вещи делали Си сложным. Языковая часть Java, я думаю, довольно проста. Библиотеки, которые вы должны искать.

Другие советы

Читая между строк, я думаю, что логика была примерно такой:

  • как правило, разработчики Java хотели упростить набор доступных типов данных
  • для повседневных целей, по их мнению, наиболее распространенной потребностью были подписанные типы данных
  • для реализации определенных алгоритмов иногда требуется арифметика без знака, но программисты, которые будут реализовывать такие алгоритмы, также будут обладать знаниями, позволяющими "обходить" выполнение арифметики без знака со знаковыми типами данных

В целом, я бы сказал, что это было разумное решение.Возможно, я бы так и сделал:

  • сделали байт без знака или, по крайней мере, предоставили альтернативы signed / unsigned, возможно, с разными именами, для этого типа данных (сделать его подписанным полезно для согласованности, но когда вам когда-нибудь понадобится подписанный байт?)
  • покончено с "коротким" (когда вы в последний раз использовали 16-битную знаковую арифметику?)

Тем не менее, с небольшим усложнением операции с беззнаковыми значениями длиной до 32 бит не так уж плохи, и большинству людей не нужно 64-битное деление или сравнение без знака.

Это более старый вопрос, и пэт вкратце упомянул char, я просто подумал, что должен подробнее остановиться на этом для других, которые будут рассматривать это в будущем.Давайте подробнее рассмотрим примитивные типы Java:

byte - 8-разрядное целое число со знаком

short - 16-разрядное целое число со знаком

int - 32-разрядное целое число со знаком

long - 64-разрядное целое число со знаком

char - 16-битный символ (целое число без знака)

Хотя char не поддерживает unsigned арифметически, это, по существу, можно рассматривать как unsigned целое число.Вам пришлось бы явно возвращать арифметические операции обратно в char, но это предоставляет вам способ указать 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--;

Да, нет прямой поддержки целых чисел без знака (очевидно, мне не пришлось бы возвращать большинство моих операций обратно в char, если бы была прямая поддержка).Однако, безусловно, существует примитивный тип данных без знака.Мне бы тоже хотелось видеть байт без знака, но я думаю, что удвоение стоимости памяти и использование вместо этого символа является жизнеспособным вариантом.


Редактировать

С JDK8 появились новые API для Long и Integer которые обеспечивают вспомогательные методы при лечении long и int значения как значения без знака.

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

Дополнительно, Гуава предоставляет ряд вспомогательных методов для выполнения аналогичных действий для целых типов, что помогает ликвидировать пробел, оставленный отсутствием встроенной поддержки для unsigned целые числа.

В Java есть типы без знака или, по крайней мере, один: char - это беззнаковое сокращение. Так что, что бы ни извинял Гослинг, на самом деле это просто его невежество, почему нет других неподписанных типов.

Также короткие типы: шорты все время используются для мультимедиа. Причина в том, что вы можете разместить 2 сэмпла в одном 32-битном беззнаковом коде и векторизовать множество операций. То же самое с 8-битными данными и беззнаковым байтом. Вы можете разместить 4 или 8 выборок в регистре для векторизации.

Как только подписанные и неподписанные целые числа смешиваются в выражении, вещи начинают запутываться, и вы, вероятно, потеряете информацию. Ограничение Java подписанными целыми числами только действительно проясняет ситуацию. Я рад, что мне не нужно беспокоиться обо всем бизнесе со знаком / без знака, хотя иногда я пропускаю 8-й бит в байте.

http://skeletoncoder.blogspot.com/ 2006/09 / Java-учебники-почему-не-unsigned.html

Этот парень говорит, потому что стандарт C определяет операции, включающие неподписанные и подписанные целые, которые должны рассматриваться как неподписанные. Это может привести к тому, что целые числа с отрицательными знаками скатятся в большое целое число без знака, что может привести к ошибкам.

Я думаю, Java хороша сама по себе, добавление unsigned усложнило бы ее без особого выигрыша.Даже при использовании упрощенной целочисленной модели большинство Java-программистов не знают, как ведут себя основные числовые типы - просто прочитайте книгу Java - головоломки чтобы понять, каких неправильных представлений вы можете придерживаться.

Что касается практических советов:

  • Если ваши значения имеют несколько произвольный размер и не вписываются в int, использовать long.Если они не вписываются в long использование BigInteger.

  • Используйте меньшие типы только для массивов, когда вам нужно сэкономить место.

  • Если вам нужно ровно 64/32/16/8 бита, используйте long/int/short/byte и перестаньте беспокоиться о знаковом бите, за исключением деления, сравнения, сдвига вправо и приведения.

Смотрите также это ответ о "переносе генератора случайных чисел с C на Java".

Благодаря JDK8 он действительно имеет некоторую поддержку для них.

Мы все еще можем увидеть полную поддержку неподписанных типов в Java, несмотря на озабоченность Гослинга.

Я знаю, что этот пост слишком старый; однако для вашего интереса в Java 8 и более поздних версиях вы можете использовать тип данных int для представления 32-разрядного целого числа без знака, которое имеет минимальное значение 0 и максимальное значение 2 32 & # 8722; 1. Используйте класс Integer для использования типа данных int в качестве целочисленных без знака и статических методов, таких как compareUnsigned () , DivUnsigned () и т. д. были добавлены в класс Integer для поддержки арифметических операций для целых чисел без знака.

Я слышал истории о том, что они должны были быть включены в оригинальную версию Java. Дуб был предшественником Java, и в некоторых спецификациях упоминалось о присвоенных значениях. К сожалению, они никогда не превращались в язык Java. Насколько кому-то удалось выяснить, что они просто не были реализованы, вероятно, из-за нехватки времени.

Однажды я проходил курс C ++ с кем-то из комитета по стандартам C ++, который подразумевал, что Java приняла правильное решение, чтобы избежать использования целых чисел без знака, потому что (1) большинство программ, которые используют целые числа без знака, могут так же хорошо справляться с целыми числами со знаком, и это более естественный с точки зрения того, как люди думают, и (2) использование целых чисел без знака приводит к простоте создания, но трудностям отладки, таким как целочисленное арифметическое переполнение и потеря значительных битов при преобразовании между типами со знаком и без знака. Если вы по ошибке вычитаете 1 из 0 с помощью целых чисел со знаком, это часто приводит к сбою вашей программы и облегчает поиск ошибки, чем если бы она сводилась к 2 ^ 32 - 1, а компиляторы и инструменты статического анализа и проверки времени выполнения должны Предположим, вы знаете, что делаете, так как решили использовать беззнаковую арифметику. Кроме того, отрицательные числа, такие как -1, часто могут представлять что-то полезное, например, поле, которое игнорируется / по умолчанию / unset, в то время как если вы используете unsigned, вам нужно зарезервировать специальное значение, например 2 ^ 32 - 1 или что-то подобное.

Давным-давно, когда память была ограничена, а процессоры не работали автоматически на 64 битах сразу, каждый бит учитывался намного больше, так что подписывание против неподписанных байтов или шорт действительно имело значение гораздо чаще и, очевидно, было правильным решением для проектирования. , Сегодня просто использовать подписанное int более чем достаточно почти во всех случаях обычного программирования, и если вашей программе действительно нужно использовать значения больше 2 ^ 31 - 1, вам все равно часто просто требуется long. Как только вы перешли на территорию использования длинных, еще сложнее найти причину, по которой вы действительно не можете обойтись с 2 ^ 63 - 1 положительным целым числом. Всякий раз, когда мы перейдем на 128-битные процессоры, это будет еще меньше проблем.

Ваш вопрос " Почему Java не поддерживает целые числа без знака "?

И мой ответ на ваш вопрос заключается в том, что Java хочет, чтобы все его примитивные типы: байт , char , short , int и long должны рассматриваться как байт , слово , слово и qword соответственно, точно так же, как в сборке, и операторы Java являются операциями со знаком для всех его примитивных типов, кроме char , но только для char они только 16-битные без знака.

Таким образом, статические методы предполагают, что операции без знака также как для 32-, так и для 64-разрядных.

Вам нужен последний класс, статические методы которого можно вызывать для операций unsigned .

Вы можете создать этот последний класс, назвать его любым именем и реализовать его статические методы.

Если вы не знаете, как реализовать статические методы, тогда эта ссылка может помочь вам.

По моему мнению, Java не похож на C ++ вообще , если он ни не поддерживает неподписанные типы , ни перегрузка операторов, поэтому я считаю, что Java следует рассматривать как совершенно другой язык как для C ++, так и для языка C.

Между прочим, в названии языков они также совершенно разные.

Так что я не рекомендую в Java набирать код, похожий на C, и вообще не рекомендую набирать код, похожий на C ++, потому что тогда в Java вы не сможете делать то, что хотите делать дальше. в C ++, то есть код не будет по-прежнему похож на C ++, и для меня это плохо - кодировать так, менять стиль посередине.

Я рекомендую писать и использовать статические методы также для подписанных операций, так что вы не увидите в коде смеси операторов и статических методов для подписанных и неподписанных операций, если только вам не нужны только подписанные операции в коде, и можно использовать только операторы.

Также я рекомендую избегать использования примитивных типов short , int и long , а также использовать word , < strong> dword и qword соответственно, и вы собираетесь вызывать статические методы для операций без знака и / или со знаком вместо использования операторов.

Если вы собираетесь выполнять только подписанные операции и использовать операторы только в коде, тогда можно использовать эти примитивные типы short , int и долго .

На самом деле word , dword и qword действительно n't существуют в языке, но вы можете создать новый класс для каждого и реализация каждого должна быть очень простой:

Класс word содержит только примитивный тип short , класс dword содержит только примитивный тип int и класс qword содержит только примитивный тип long . Теперь все неподписанные и подписанные методы могут быть статическими или нет по вашему выбору, вы можете реализовать в каждом классе, то есть все 16-битные операции как без знака, так и со знаком, указав имена значений в классе word , все 32-разрядные операции, как без знака, так и со знаком, дают значащие имена в классе dword , и все 64-разрядные операции, как без знака, так и со знаком, дают значащие имена в классе qword .

Если вам не нравится давать слишком много разных имен для каждого метода, вы всегда можете использовать перегрузку в Java, хорошо бы прочитать, что Java n't тоже это удалила!

Если вы хотите методы, а не операторы для 8 би

Потому что unsigned типаж - это чистое зло.

Тот факт, что в C unsigned - int производит unsigned это еще большее зло.

Вот краткий обзор проблемы, которая не раз мучила меня:

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

Вы уже заметили ошибку?Признаюсь, я увидел это только после того, как подключился к отладчику.

Потому что n имеет неподписанный тип size_t полное выражение n - (rays.size() - 1) / 2 оценивается как unsigned.Это выражение предназначено для того, чтобы быть подписанный положение о nй луч из среднего:1-й луч от среднего с левой стороны будет иметь положение -1, 1-й луч справа будет иметь положение +1 и т.д.После взятия значения abs и умножения на delta угол, под которым я бы получил угол между nй луч и средний.

К сожалению для меня, приведенное выше выражение содержало значение evil без знака, и вместо того, чтобы оценивать, скажем, -1, оно оценивалось как 2 ^ 32-1.Последующее преобразование в double исправил ошибку.

После одной-двух ошибок, вызванных неправильным использованием unsigned в арифметике человек должен начать задаваться вопросом, стоит ли дополнительный бит, который он получает, дополнительных хлопот.Я стараюсь, насколько это возможно, избегать любого использования unsigned вводит в арифметику, хотя по-прежнему использует ее для неарифметических операций, таких как двоичные маски.

В спецификации 'C' есть несколько драгоценных камней, которые Java отбросила по прагматическим причинам, но которые медленно набирают обороты с учетом требований разработчиков (замыканий и т. д.).

Я упоминаю первый, потому что он связан с этим обсуждением; соответствие значений указателя целочисленной арифметике без знака. И, в связи с темой этой темы, трудность поддержания семантики без знака в мире Java со знаком.

Я бы предположил, что если бы кто-то из альтер-эго Денниса Ричи посоветовал команде разработчиков Гослинга, он предложил бы присвоить Signed "ноль на бесконечности", чтобы все запросы смещения адресов сначала добавляли свой АЛГЕБРАИЧЕСКИЙ РАЗМЕР КОЛЬЦА, чтобы избежать отрицательные значения.

Таким образом, любое смещение, выброшенное в массив, никогда не может генерировать SEGFAULT. Например, в инкапсулированном классе, который я называю RingArray типа double, который нуждается в неподписанном поведении - в «само вращающемся цикле». контекст:

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

Приведенный выше RingArray никогда не получит отрицательный индекс, даже если злоумышленник попытается это сделать. Помните, что существует также много законных запросов для запроса предыдущих (отрицательных) значений индекса.

Примечание: внешний модуль% отменяет ссылки на законные запросы, тогда как внутренний модуль% маскирует явную злобу от негативов, более негативных, чем -модул. Если бы это когда-либо появилось в Java + .. + 9 || 8 + .. + спецификации, тогда проблема действительно превратилась бы в «программиста, который не может« самостоятельно вращаться ». FAULT».

Я уверен, что так называемый «дефицит» в Java unsigned int можно восполнить с помощью приведенного выше однострочного текста.

PS: просто для того, чтобы дать контекст вышеупомянутой обработке RingArray, вот кандидатная операция 'set', соответствующая вышеописанной операции элемента 'get':

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
}

Я могу вспомнить один неприятный побочный эффект. Во встроенных базах данных Java число идентификаторов, которые вы можете иметь с полем 32-битного идентификатора, равно 2 ^ 31, а не 2 ^ 32 (~ 2 миллиарда, а не ~ 4 миллиарда).

Причина, ИМХО, в том, что они слишком ленивы, чтобы реализовать / исправить эту ошибку.Предполагая, что программисты на C / C ++ не понимают unsigned, structure, union, bit flag...Это просто нелепо.

Возможно, вы разговаривали с программистом на basic / bash / java, который вот-вот начнет программировать на C, без каких-либо реальных знаний этого языка, или вы просто бредите своим умом.;)

когда вы каждый день имеете дело с форматированием либо из файла, либо с аппаратным обеспечением, вы начинаете задаваться вопросом, о чем, черт возьми, они думали.

Хорошим примером здесь была бы попытка использовать байт без знака в качестве самовращающегося цикла.Для тех из вас, кто не понимает последнего предложения, объясню, с какой стати вы называете себя программистом.

Постоянный ток

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top