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

java language-design unsigned integer

110166 просмотра

16 ответа

46341 Репутация автора

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

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

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

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

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

Автор: dsimcha Источник Размещён: 10.01.2009 01:35

Ответы (16)


12 плюса

9432 Репутация автора

http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html

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

Автор: akatakritos Размещён: 10.01.2009 01:42

190 плюса

68828 Репутация автора

Решение

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

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

Автор: Uri Размещён: 10.01.2009 01:43

4 плюса

589 Репутация автора

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

Автор: Rob Ottaway Размещён: 10.01.2009 01:45

14 плюса

67998 Репутация автора

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

Автор: Bombe Размещён: 10.01.2009 02:05

50 плюса

19090 Репутация автора

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

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

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

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

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

Автор: Neil Coffey Размещён: 10.01.2009 04:29

11 плюса

46683 Репутация автора

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

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

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

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

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

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

Автор: starblue Размещён: 10.01.2009 08:39

-2 плюса

1201 Репутация автора

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

Автор: mike g Размещён: 11.01.2009 11:56

15 плюса

191 Репутация автора

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

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

Автор: pat Размещён: 12.10.2009 07:20

18 плюса

4211 Репутация автора

Это более старый вопрос, и Пэт кратко упомянул 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, если бы была прямая поддержка). Тем не менее, безусловно, существует неподписанный примитивный тип данных. Я хотел бы также видеть неподписанный байт, но я полагаю, что удвоение стоимости памяти и использование char - приемлемый вариант.


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

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

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

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

Автор: Jyro117 Размещён: 27.07.2011 08:22

-8 плюса

61 Репутация автора

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

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

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

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

ОКРУГ КОЛУМБИЯ

Автор: Denis Co Размещён: 10.03.2012 09:37

6 плюса

7828 Репутация автора

С JDK8 у него есть некоторая поддержка для них.

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

Автор: John Hascall Размещён: 24.02.2013 03:30

6 плюса

1482 Репутация автора

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

Автор: Morteza Adi Размещён: 30.01.2014 09:18

1 плюс

3446 Репутация автора

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

Тот факт, что в Си 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 и т.д. После взяв значение abs и умножив на deltaугол, я получу угол между nсредним и третьим лучом.

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

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

Автор: Michael Размещён: 02.02.2016 10:36

4 плюса

53 Репутация автора

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

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

Автор: Jonathan Размещён: 01.11.2016 07:13

2 плюса

0 Репутация автора

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

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

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

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

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

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

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

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

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

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

Кроме того, я рекомендую не использовать короткие , Int и длинные примитивные типы, а также использовать слово , двойное слово и QWORD соответственно вместо этого, и вы об вызвать статические методы для неподписанных операций и / или подписаны операции вместо использования операторов.

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

На самом деле слово , двойное слово и QWORD ничего не существует в языке, но вы можете создать новый класс для всех и реализация каждого должно быть очень легко:

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

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

Если вам нужны методы, а не операторы для 8-битных операций со знаком и методы для 8-битных операций без знака, у которых вообще нет операторов, то вы можете создать класс Byte (обратите внимание, что первая буква «B» - это заглавная, так что это не примитивный тип byte ) и реализует методы этого класса.

О передаче по значению и передаче по ссылке:

Если я не ошибаюсь, как в C #, примитивные объекты передаются по значению естественным образом, но объекты класса передаются по ссылке естественным образом, что означает, что объекты типа Byte , word , dword и qword будут передаваться по ссылке, а не по значению по умолчанию. Я хочу Java имела STRUCT объектов в C # есть, так что все Byte , слово , двойное слово и QWORD могут быть реализованы как структура вместо классапоэтому по умолчанию они передаются по значению, а не по ссылке по умолчанию, как и любой объект структуры в C #, например, примитивные типы, передаются по значению, а не по ссылке по умолчанию, а потому что Java хуже, чем C #, и мы имеем чтобы справиться с этим, то есть только классы и интерфейсы, которые передаются по ссылке, а не по значению по умолчанию. Поэтому, если вы хотите передать объекты Byte , word , dword и qword по значению, а не по ссылке, как любой другой объект класса в Java, а также в C #, вам придется просто использовать конструктор копирования и все.

Это единственное решение, о котором я могу думать. Я просто хотел бы, чтобы я мог просто определить тип примитива для word, dword и qword, но Java не поддерживает typedef и не использует вообще, в отличие от C #, который поддерживает использование , что эквивалентно typedef в C.

О выходе:

Для одной и той же последовательности битов вы можете распечатать их разными способами: в двоичном, десятичном (например, значение% u в C printf), в восьмеричном (например, значение% o в C printf), в шестнадцатеричном (например, значение% x в C printf) и как целое число (например, значение% d в C printf).

Обратите внимание, что C printf не знает тип переменных, передаваемых в качестве параметров функции, поэтому printf знает тип каждой переменной только из объекта char *, переданного первому параметру функции.

Таким образом, в каждом из классов: Byte , word , dword и qword вы можете реализовать метод print и получить функциональность printf, даже если примитивный тип класса подписан, вы все равно можете напечатать его как unsigned, следуя некоторому алгоритму, включающему логические и сдвиговые операции для получения цифр для вывода на выход.

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

Это все, что я могу ответить на ваш вопрос и предложить вам.

Автор: user2133061 Размещён: 06.08.2017 08:44

0 плюса

19 Репутация автора

В спецификации «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 + .. + spec, тогда проблема действительно превратилась бы в «программиста, который не может« самостоятельно вращать »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
}
Автор: MKhomo Размещён: 27.06.2019 03:03
Вопросы из категории :
32x32