Почему так много числовых типов (bit, int, float, double, long)?

9

Я изучил PHP, Java и C. Теперь мне интересно, почему существует так много типов числовых типов данных, как bit, int, float, double и long. Почему бы не сделать только один тип для чисел?

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

GusDeCooL
источник
6
В дополнение к ответу HorusKol: типы «float» и «integer» по своей сути различны. Число с плавающей запятой может содержать очень большие числа, но с увеличением размера числа точность уменьшается. Эта неточность из-за способа хранения поплавков. В отличие от этого, диапазон значений, которые вы можете хранить в целых числах, довольно ограничен, но значение всегда является точным, поэтому сравнивать значения гораздо проще. Кроме того, есть два различных типа поведения с делением - целые числа автоматически усечаются до ближайшего целого числа, а плавающие - нет. Каждое из этих поведений полезно для разных ситуаций.
Кампу
Javascript имеет только один тип чисел на поверхности.
Esailija
@kampu: На самом деле, во многих языках целые числа могут хранить любое число, пока (виртуальная) память достаточно велика, чтобы представлять его.
Йорг Миттаг
1
@ JörgWMittag: Однако этот вопросник явно говорит о статических языках, а не о динамических языках, таких как, например, Python. Сам CPython реализует целое число «неограниченный диапазон» в виде массива 32-битных чисел, причем последний бит в каждом int используется для указания того, есть ли еще биты. Также целые числа могут хранить только любое целое число. Это означает, что число с плавающей запятой с бесконечным хранилищем может хранить значения с точностью (бесконечность aleph one), в то время как целые числа могут хранить значения только с точностью ( бесконечность aleph zero ).
Кампу
@kampu: Поскольку все числа представлены сериями битов, даже с бесконечной памятью, всегда будет взаимно однозначное соответствие между числами с плавающей запятой и целыми числами. Так что я не думаю, что aleph one приходит к вопросу.
Приходите с

Ответы:

17

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

1. Сохранение памяти

for(long k=0;k<=10;k++)
{
    //stuff
}

Зачем использовать long, если он может быть просто целым числом или даже байтом? Таким образом, вы действительно сэкономите несколько байтов памяти.

2. Числа с плавающей точкой и целые числа хранятся по-разному в компьютере

Предположим, что у нас есть число 22, хранящееся в целом числе. Компьютер хранит этот номер в памяти в двоичном виде как:

0000 0000 0000 0000 0000 0000 0001 0110

Если вы не знакомы с системой двоичных чисел, это может быть представлено в научной нотации как: 2 ^ 0 * 0 + 2 ^ 1 * 1 + 2 ^ 2 * 1 + 2 ^ 3 * 0 + 2 ^ 4 * 1 + 2 ^ 5 * 0 + ... + 2 ^ 30 * 0. Последний бит может или не может использоваться для указания, является ли число отрицательным (в зависимости от того, является ли тип данных подписанным или беззнаковым).

По сути, это просто сумма 2 ^ (разрядное место) * значение.

Это меняется, когда вы ссылаетесь на значения, включающие десятичную точку. Предположим, у вас есть число 3.75 в десятичном виде. Это упоминается как 11.11 в двоичном формате. Мы можем представить это как научное обозначение как 2 ^ 1 * 1 + 2 ^ 0 * 1 + 2 ^ -1 * 1 + 2 ^ -2 * 1 или, нормализовано, как 1.111 * 2 ^ 2

Однако компьютер не может сохранить это: у него нет явного метода выражения этой двоичной точки (версия десятичной точки в двоичной системе счисления). Компьютер может хранить только 1 и 0. Вот где приходит тип данных с плавающей точкой.

Предполагая, что sizeof (float) составляет 4 байта, тогда у вас есть 32 бита. Первый бит присваивается «знаковый бит». Там нет беззнаковых поплавков или двойников. Следующие 8 бит используются как «показатель степени», а последние 23 бита используются как «значение» (или иногда их называют мантиссой). Используя наш пример 3.75, наш показатель будет равен 2 ^ 1, а наше значение равно 1.111.

Если первый бит равен 1, число является отрицательным. Если нет, то положительный. Экспонента модифицируется чем-то, что называется «смещением», поэтому мы не можем просто сохранить «0000 0010» в качестве экспоненты. Смещение для числа с плавающей запятой одинарной точности равно 127, а смещение для двойной точности (в этом случае двойной тип данных получает свое имя) равно 1023. Последние 23 бита зарезервированы для значимого. Значение и просто значения ПРАВА нашей двоичной точки.

Наш показатель будет смещением (127) + показатель (1) или представлен в двоичном виде

1000 0000

Наше значение и будет:

111 0000 0000 0000 0000 0000

Следовательно, 3.75 представляется как:

0100 0000 0111 0000 0000 0000 0000 0000

Теперь давайте посмотрим на число 8, представленное как число с плавающей запятой и как целое число:

0100 0001 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 1000

Как в мире компьютер собирается добавить 8,0 и 8? Или даже умножить их !? Компьютеры (точнее, компьютеры x86) имеют разные части ЦП, которые добавляют числа с плавающей запятой и целые числа.

cpmjr123
источник
3
3) хотя и редко встречающаяся проблема: операции над числами, превышающими размер слов компьютера, выполняются медленнее.
Лорен Печтел
6

Еще до того, как у нас были гигабайтные системы (или в современных встраиваемых системах, таких как Arduino), память была на премиум-уровне, и поэтому были реализованы сокращенные методы, чтобы указать, сколько памяти будет занимать конкретное число - BIT прост - первоначально он занимал только 1 бит памяти.

Другие размеры данных и имена варьируются в зависимости от системы. В 32-битной системе INT (или MEDIUMINT) обычно составляет 2 байта, LONGINT - 4 байта, а SMALLINT - один байт. В 64-битных системах LONGINT может быть установлен на 8 байтов.

Даже сейчас - особенно в приложениях баз данных или в программах, которые работают на серверах в нескольких экземплярах (например, серверные сценарии на веб-сайтах) - вы должны быть осторожны с тем, что вы выбираете. Выбор целого числа шириной 2, 4 или 8 байтов для хранения значений от 0 до 100 (который может уместиться в один байт) невероятно расточителен, если у вас есть таблица базы данных с миллионами записей.

Дополнительная информация: https://en.wikipedia.org/wiki/Integer_(computer_science)

HorusKol
источник
хороший ответ +1.
Vinay
7
Не только «назад», но и «сейчас, когда система мала». На устройстве размером с Arduino нужно быть экономичным.
9000
1
Какая система использовала только 1 бит для хранения немного? биты обычно не адресуемы напрямую
jk.
1
это верно для многих архитектур - но биты были непосредственно адресуемыми в действительно старых системах и даже в некоторых более поздних встроенных системах (некоторые контроллеры, которые я запрограммировал только 10 лет назад, работали с битами - у них было только около 64 адресуемых местоположений определенной ширины). В настоящее время, я думаю, компиляторы решают это и помещают их в байтовые массивы.
ХорусКол
Я думаю, что основным фактором является производительность процессора и производительность, а не проблемы с памятью
Джеймс
4

В дополнение к отличным замечаниям cpmjr123 о нехватке памяти и точности, а также о компромиссе диапазонов, они также потенциально являются компромиссом ЦП.

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

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

JK.
источник
4

Возможно, самое главное, что на самом деле существует три разных основных типа чисел.

целое число, фиксированное десятичное число и число с плавающей запятой.

Все они ведут себя по-разному.

Простая операция, такая как 7/2, может дать ответы 3, 3,50 и 3,499 в зависимости от используемого типа данных.

«фиксированный десятичный» - это тип Золушки, он изначально поддерживается только в нескольких языках, таких как COBOL и VisualBasic. Это не представляет большого интереса для компьютерных ученых, но жизненно важно для любого, кто представляет набор счетов или рассчитывает налог с продаж на счете.

Джеймс Андерсон
источник
Я бы разделил их по-разному: дискретные числа, приблизительные числа и оберточные алгебраические кольца. Типичные примеры в C будет int, floatи unsigned int, соответственно. Типы с фиксированной запятой являются подкатегорией дискретных типов, но алгебраические кольца принципиально отличаются от чисел [необходимость путаницы в отношении типов без знака в C связана с тем, что они в основном ведут себя как кольца, а не числа, но не вполне согласованы] ,
суперкат
3

Есть ли какая-то выгода для них?

Конечно. Есть преимущества. В мире компьютеров память является одной из самых важных вещей для рассмотрения. Какая польза от наличия памяти объемом 2 КБ, если данные могут умещаться менее чем 1 КБ? , Оптимизации должны быть там. Если вы используете больше памяти, это, очевидно, убивает скорость вашего компьютера в определенный момент. Вам действительно нравится это иметь? Без прав...?

int - 2 bytes (16 bits)

long - 4 bytes (32 bits)

long long - 8 bytes (64 bits)

float - 4 bytes

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

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

Виней
источник
3

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

Целые числа перечислимы, а не с плавающей точкой и т. Д.

Фактически число с плавающей запятой / двойное число - это структура, которая объединяет два целых поля: мантиссу и экспоненту. Комплексные числа (которые вы исключили из рассмотрения) еще более сложны.

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

с-улыбка
источник
Я не знаком с упомянутыми вами "комплексными числами". Можешь объяснить дальше?
cpmjr123
проверьте это: en.wikipedia.org/wiki/Complex_number
c-smile
Мне известны комплексные числа в виде + би. Я просил больше информации о том, как компьютер хранит комплексные числа. Насколько мне известно, нет примитивных типов данных, которые поддерживают это.
cpmjr123
Комплексные числа обычно хранятся в виде двух значений с плавающей запятой, а именно их a(действительная часть) и b(мнимая часть). CPU обычно не реализуют встроенную поддержку операций с комплексными числами, хотя CPU могут реализовывать ускоренные инструкции умножения-сложения для операций над парами значений, таких как (a b + c d) и (a b-c d).
Rwong
1
Кроме того, многие языки имеют некоторые типы, поведение которых в значительной степени определяется как поведение алгебраического кольца с оберткой (например, если переменная типа uint16_tсодержит 65535, при увеличении ее значение будет равно 0). В идеале языки должны иметь четко разделенные типы для представления оберточных алгебраических колец и чисел (позволяя перехватывать числа, которые переполняются, в то же время позволяя коду легко выполнять операции с вещами, которые должны переноситься).
суперкат
-1

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

Представьте, что вы хотите отсортировать (длинный) массив. Например, в C:

int numbers[100000000];

Итак, у нас есть 100 миллионов номеров.

Если каждое число имеет длину только один байт (поэтому используется unsigned charвместо int), то для этого требуется 100 миллионов байтов пространства.

Если вы используете double, то это обычно 8 байтов на число, так что 800 миллионов байтов пространства.

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

Инго Блэкман
источник