Почему Java не поддерживает целые числа без знака? - PullRequest
357 голосов
/ 10 января 2009

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

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

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

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

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

Ответы [ 15 ]

3 голосов
/ 01 ноября 2016

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

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

2 голосов
/ 06 августа 2017

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если я не ошибаюсь, как в C #, примитивные объекты передаются по значению естественным образом, но объекты класса передаются по ссылке естественным образом, что означает, что объекты типа Byte , word , dword и qword будут передаваться по ссылке, а не по значению по умолчанию. Я бы хотел, чтобы у Java было struct объектов, как у C #, так что все байт , слово , dword и qword могут быть реализованы, чтобы быть struct вместо класс , поэтому по умолчанию они передаются по значению, а не по ссылке по умолчанию, как и любой объект структуры в 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 *, переданного первому параметру функции.

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

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

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

1 голос
/ 03 февраля 2016

Потому что 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 -ым лучом и средним.

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

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

0 голосов
/ 11 марта 2012

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

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

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

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

DC

0 голосов
/ 12 января 2009

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

...