Просто для удовольствия я играл с представлением поплавков, следуя определениям из стандарта C99, и я написал код ниже.
Код печатает двоичное представление поплавков в 3 отдельных группах
SIGN EXPONENT FRACTION
, и после этого он печатает сумму, которая при суммировании с достаточной точностью покажет значение, которое действительно существует в аппаратном обеспечении.
Поэтому, когда вы пишете float x = 999...
компилятор преобразует это число в битовое представление, напечатанное функцией xx
, так что сумма, напечатанная функцией yy
, будет равна заданному числу.
В действительности эта сумма является только приближение. Для числа 999,999,999 компилятор будет вставлять в бит представление float число 1,000,000,000
После кода я присоединяю консольный сеанс, в котором я вычисляю сумму терминов для обеих констант (минус PI и 999999999) который действительно существует в аппаратном обеспечении, вставленном там компилятором.
#include <stdio.h>
#include <limits.h>
void
xx(float *x)
{
unsigned char i = sizeof(*x)*CHAR_BIT-1;
do {
switch (i) {
case 31:
printf("sign:");
break;
case 30:
printf("exponent:");
break;
case 23:
printf("fraction:");
break;
}
char b=(*(unsigned long long*)x&((unsigned long long)1<<i))!=0;
printf("%d ", b);
} while (i--);
printf("\n");
}
void
yy(float a)
{
int sign=!(*(unsigned long long*)&a&((unsigned long long)1<<31));
int fraction = ((1<<23)-1)&(*(int*)&a);
int exponent = (255&((*(int*)&a)>>23))-127;
printf(sign?"positive" " ( 1+":"negative" " ( 1+");
unsigned int i = 1<<22;
unsigned int j = 1;
do {
char b=(fraction&i)!=0;
b&&(printf("1/(%d) %c", 1<<j, (fraction&(i-1))?'+':')' ), 0);
} while (j++, i>>=1);
printf("*2^%d", exponent);
printf("\n");
}
void
main()
{
float x=-3.14;
float y=999999999;
printf("%lu\n", sizeof(x));
xx(&x);
xx(&y);
yy(x);
yy(y);
}
Вот сеанс консоли, в котором я вычисляю реальное значение float, которое существует в аппаратном обеспечении. Я использовал bc
для печати суммы терминов, выводимых основной программой. Можно вставить эту сумму в python repl
или что-то подобное.
-- .../terra1/stub
@ qemacs f.c
-- .../terra1/stub
@ gcc f.c
-- .../terra1/stub
@ ./a.out
sign:1 exponent:1 0 0 0 0 0 0 fraction:0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1
sign:0 exponent:1 0 0 1 1 1 0 fraction:0 1 1 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
negative ( 1+1/(2) +1/(16) +1/(256) +1/(512) +1/(1024) +1/(2048) +1/(8192) +1/(32768) +1/(65536) +1/(131072) +1/(4194304) +1/(8388608) )*2^1
positive ( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
-- .../terra1/stub
@ bc
scale=15
( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
999999999.999999446351872
Вот и все. Фактически значение 999999999
999999999.999999446351872
Вы также можете проверить с помощью bc
, что -3.14 также возмущено. Не забудьте установить коэффициент scale
в bc
.
Отображаемая сумма - это то, что внутри аппаратного обеспечения. Значение, которое вы получаете, вычисляя его, зависит от установленного вами масштаба. Я установил коэффициент scale
равным 15. Математически, с бесконечной точностью, кажется, что это 1 000 000 000.
Как отвечали другие, «стандарты» оставляют большинство деталей «реализацией» и указывают только, что тип «char» находится в «char_bis» шириной и что «char & lt; = short & lt; = int & lt; = long & lt; = long long "(float и double в значительной степени соответствуют стандарту IEEE с плавающей запятой, а long double обычно является таким же, как double, но может быть больше для более современных реализаций).
Часть причин не очень точных и точных значений заключается в том, что такие языки, как C / C ++, были переносимы на большое количество аппаратных платформ - включая компьютерные системы, в которых может быть размер слова «char» 4-битные или 7-битные или даже некоторые значения, отличные от компьютеров «8- / 16- / 32- / 64-бит», которым подвергается средний пользователь домашнего компьютера. (Размер слова здесь означает, сколько битов в ширину система работает нормально - Опять же, это не всегда 8 бит, как могут ожидать пользователи домашних компьютеров.)
Если вам действительно нужен объект (в смысле из серии бит, представляющих интегральное значение) определенного количества бит, большинство компиляторов имеют некоторый способ указания этого; Но, как правило, он не переносится даже между компиляторами, производимыми компанией ame, но для разных платформ. Некоторые стандарты и методы (особенно limits.h и т. П.) Достаточно распространены, что большинство компиляторов будут иметь поддержку для определения типа наилучшего соответствия для определенного диапазона значений, но не для количества используемых битов. (То есть, если вы знаете, что вам нужно хранить значения от 0 до 127, вы можете определить, что ваш компилятор поддерживает 8-битный тип «int8», который будет достаточно большим, чтобы удерживать весь желаемый диапазон, но не что-то вроде «int7», который был бы точным совпадением для 7-битов.)
Примечание. Многие исходные пакеты Un * x используют сценарий «./configure», который будет определять возможности компилятора / системы и выводит подходящий Makefile и config.h. Вы можете изучить некоторые из этих сценариев, чтобы увидеть, как они работают, и как они исследуют возможности компилятора / системы и следуют за ними.
Я замечаю, что все остальные ответы здесь сосредоточены почти исключительно на интегральных типах, в то время как вопросник также задал вопрос о плавающих точках.
Я не думаю, что это требует стандарт C ++, но компиляторы для наиболее распространенные платформы в наши дни обычно следуют стандарту IEEE754 для их чисел с плавающей запятой. Этот стандарт указывает четыре типа двоичной с плавающей запятой (а также некоторые форматы BCD, которых я никогда не видел в компиляторах на C ++):
Как это отображается на типы C ++, затем ? Обычно float
использует одинарную точность; таким образом, sizeof(float) = 4
. Затем double
использует двойную точность (я полагаю, что это источник имени double
), а long double
может быть двойной или четырехкратной точностью (это четверка в моей системе, но в 32-битных системах она может быть двойной) , Я не знаю каких-либо компиляторов, которые предлагают плавающие точки с половинной точностью.
Таким образом, это обычное:
sizeof(float)
= 4 sizeof(double)
= 8 sizeof(long double)
= 8 или 16 В стандарте C ++ не указывается размер интегральных типов в байтах, но он указывает минимальные диапазоны, которые они должны удержать. Вы можете вывести минимальный размер в битах из требуемого диапазона. Вы можете вывести минимальный размер в байтах из этого и значение макроса CHAR_BIT
, которое определяет количество бит в байте (во всех, кроме самых неясных платформах, это 8, и оно не может будет меньше 8).
Еще одно ограничение для char
заключается в том, что его размер всегда равен 1 байту или CHAR_BIT
битам (отсюда и название).
Минимальные диапазоны , требуемый стандартом (стр. 22):
и диапазоны типов данных на MSDN :
signed char
: От -127 до 127 (обратите внимание, не от -128 до 127, это предполагает платформы с 1-м дополнением и знаками и знаками) unsigned char
: от 0 до 255 char
: тот же диапазон, что и signed char
или unsigned char
, , определенные реализацией signed short
: от -32767 до 32767 unsigned short
: От 0 до 65535 signed int
: от -32767 до 32767 unsigned int
: от 0 до 65535 signed long
: от -2147483647 до 2147483647 unsigned long
: от 0 до 4294967295 signed long long
: -9223372036854775807 до 9223372036854775807 unsigned long long
: от 0 до 18446744073709551615 AC Реализация ++ (или C) может определять размер типа в байтах sizeof(type)
до любого значения, пока
sizeof(type) * CHAR_BIT
оценивается как количество бит, достаточно высокое, чтобы содержат требуемые диапазоны, а sizeof(int) <= sizeof(long)
). Фактические диапазоны, специфичные для реализации, можно найти в заголовке <limits.h>
в C или <climits>
в C ++ (или даже лучше, templated std::numeric_limits
в <limits>
.
Например, вы найдете максимальный диапазон для int
:
C:
#include <limits.h>
const int min_int = INT_MIN;
const int max_int = INT_MAX;
C ++:
#include <limits>
const int min_int = std::numeric_limits<int>::min();
const int max_int = std::numeric_limits<int>::max();
Для чисел с плавающей запятой существует стандарт (IEEE754) : поплавки - 32 бит, а удваивается 64. Это аппаратный стандарт, а не стандарт C ++, поэтому компиляторы теоретически могут определить float и double на какой-то другой размер, но на практике я никогда не видел архитектуры, которая использовала что-то другое.
Нет, для типовых размеров нет стандарта. Стандарт требует только того, чтобы:
sizeof(short int) <= sizeof(int) <= sizeof(long int)
Лучшее, что вы можете сделать, если вы хотите, чтобы переменные фиксированных размеров использовали макросы, такие как:
#ifdef SYSTEM_X
#define WORD int
#else
#define WORD long int
#endif
Затем вы можете использовать WORD, чтобы определить ваши переменные. Не то, чтобы мне это нравилось, но это самый портативный способ.
От Alex B В стандарте C ++ не указывается размер интегральных типов в байтах, но он указывает минимальные диапазоны, которые они должны удержать. Вы можете вывести минимальный размер в битах из требуемого диапазона. Вы можете вывести минимальный размер в байтах из этого и значение макроса CHAR_BIT, определяющего количество бит в байте (во всех, кроме самых неясных платформ, это 8, и оно не может быть меньше 8).
Еще одно ограничение для char состоит в том, что его размер всегда 1 байт или бит CHAR_BIT (отсюда и название).
Минимальные диапазоны, требуемые стандартом (стр. 22):
и диапазоны типов данных в MSDN:
подписанный символ: от -127 до 127 (обратите внимание, не от -128 до 127, это соответствует платформам с добавлением 1). unsigned char: от 0 до 255 «простой» символ : От -127 до 127 или от 0 до 255 (зависит от знака по умолчанию): короткое: -32767 до 32767 без знака short: от 0 до 65535, подписанное int: -32767 до 32767 unsigned int: от 0 до 65535, подписанное длиной: -2147483647 до 2147483647 без знака длинный: от 0 до 4294967295 подписан длинный длинный: -9223372036854775807 до 9223372036854775807 unsigned long long: от 0 до 18446744073709551615 Реализация C ++ (или C) может определять размер типа в байтах sizeof (type) для любого значения, до тех пор, пока
выражение sizeof (type) * CHAR_BIT оценивает количество бит, достаточное для того, чтобы содержать требуемые диапазоны, и упорядочение типа остается в силе (например, sizeof (int) & lt; = sizeof (long)). Фактические диапазоны конкретных реализаций можно найти в заголовке на C или в C ++ (или даже лучше, в шаблоне std :: numeric_limits в заголовке).
Например, таким образом вы найдете максимальный диапазон для int:
C:
#include <limits.h>
const int min_int = INT_MIN;
const int max_int = INT_MAX;
C ++:
#include <limits>
const int min_int = std::numeric_limits<int>::min();
const int max_int = std::numeric_limits<int>::max();
Это правильно, однако вы также были правы в том, что: char: 1 byte short: 2 байта int: 4 байта: 4 байта float: 4 байта double: 8 байт
Поскольку 32-битные архитектуры по-прежнему по умолчанию и наиболее часто используются, и они сохранили эти стандартные размеры, поскольку pre -32-битные дни, когда память была менее доступной, а для обратной совместимости и стандартизации она осталась прежней. Даже 64-битные системы имеют тенденцию использовать их и имеют расширения / модификации. Пожалуйста, обратитесь к этому для получения дополнительной информации:
На практике такой вещи нет. Часто вы можете ожидать, что std::size_t
будет представлять собственный целочисленный размер без знака в текущей архитектуре. т.е. 16-разрядный, 32-разрядный или 64-разрядный, но это не всегда так, как указано в комментариях к этому ответу.
Что касается всех других встроенных типов, действительно зависит от компилятора. Вот два отрывка из текущего рабочего проекта последнего стандарта C ++:
Существует пять стандартных стандартных целочисленных типов: подписанный char, short int, int, long int и long long int. В этом списке каждый тип содержит как минимум столько же памяти, сколько и предшествующие ему в списке.
Для каждого стандартного целочисленного типа со знаком существует соответствующий (но другой) стандартный беззнаковый целочисленный тип: unsigned char, unsigned short int, unsigned int, unsigned long int и unsigned long long int, каждый из которых занимает один и тот же объем памяти и имеет те же требования к выравниванию.
blockquote>Если вы хотите вы можете статически (время компиляции) утверждать размер этих основных типов. Это заставит людей подумать о портировании вашего кода, если меняются значения sizeof.
Для 32-битных систем стандартом de facto является ILP32, то есть int
, long
и указатель - все 32-битные величины.
Для 64-битных систем, основным стандартом Unix de facto является LP64 - long
, а указатель - 64-битный (но int
- 32-разрядный). 64-битный стандарт Windows - LLP64 - long long
, а указатель - 64-разрядный (но long
и int
оба 32-разрядные).
В свое время некоторые Unix-системы использовали ILP64.
Ни один из этих фактических стандартов не законодательно закреплен стандартом C (ISO / IEC 9899: 1999), но все они разрешены им.
И, по определению, sizeof(char)
- 1
, несмотря на тест в скрипте настройки Perl.
Обратите внимание, что были машины (Crays), где CHAR_BIT
было намного больше 8. Это означало, что IIRC, что sizeof(int)
также было 1, поскольку оба char
и int
были 32-битными.
Нам разрешено определять синоним типа, чтобы мы могли создать собственный «стандарт».
На машине, в которой sizeof (int) == 4, мы можем определить:
typedef int int32;
int32 i;
int32 j;
...
Итак, когда мы передаем код на другую машину, на самом деле размер которой длинен int is 4, мы можем просто переопределить единственное вхождение int.
typedef long int int32;
int32 i;
int32 j;
...
Стандарт.
Стандарт C90 требует, чтобы стандарт
sizeof(short) <= sizeof(int) <= sizeof(long)
C99 требовал, чтобы
sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
Вот спецификации C99 . Page 22 детализируют размеры разных интегральных типов.
Вот размеры (бит) типа int для платформ Windows:
Type C99 Minimum Windows 32bit
char 8 8
short 16 16
int 16 32
long 32 32
long long 64 64
Если вы заинтересованы в переносимости или хотите, чтобы имя типа отображало размер, вы можно просмотреть заголовок <inttypes.h>
, где доступны следующие макросы:
int8_t
int16_t
int32_t
int64_t
int8_t
гарантированно будет 8 бит, а int16_t
гарантированно будет 16 бит и т. д.
Существует четыре типа целых чисел, основанных на размере:
Как уже упоминалось, размер должен отражать текущую архитектуру. Вы можете взять пик в limits.h
, если хотите посмотреть, как ваш текущий компилятор обрабатывает вещи.
1) Таблица N1 в статье « Забытые проблемы разработки 64-битных программ "
2) " Модель данных "
Если вас интересует чистое решение на C ++, я использовал шаблоны и только стандартный код C ++ для определения типов во время компиляции на основе их размера бит. Это делает решение переносимым через компиляторы.
Идея очень проста: создайте список, содержащий типы char, int, short, long, long long (подписанные и неподписанные версии) и сканирование списка и по использование шаблона numeric_limits выбирает тип с заданным размером.
Включая этот заголовок, вы получаете 8 типов stdtype :: int8, stdtype :: int16, stdtype :: int32, stdtype :: int64, stdtype :: uint8 , stdtype :: uint16, stdtype :: uint32, stdtype :: uint64.
Если какой-либо тип не может быть представлен, он будет оцениваться как stdtype :: null_type, также объявленный в этом заголовке.
КОДЕКС НИЖЕ НЕ ПРЕДОСТАВЛЯЕТСЯ БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ПОЖАЛУЙСТА, ДВОЙНАЯ ПРОВЕРКА. Я НОВЫЙ В МЕТАПРОГРАММИРОВАНИИ СЛИШКОМ, ЧУВСТВУЮТ, ЧТОБЫ ИЗМЕНИТЬ И ИСПРАВИТЬ ЭТОТ КОД. Протестировано с DevC ++ (поэтому версия gcc около 3.5)
#include <limits>
namespace stdtype
{
using namespace std;
/*
* THIS IS THE CLASS USED TO SEMANTICALLY SPECIFY A NULL TYPE.
* YOU CAN USE WHATEVER YOU WANT AND EVEN DRIVE A COMPILE ERROR IF IT IS
* DECLARED/USED.
*
* PLEASE NOTE that C++ std define sizeof of an empty class to be 1.
*/
class null_type{};
/*
* Template for creating lists of types
*
* T is type to hold
* S is the next type_list<T,S> type
*
* Example:
* Creating a list with type int and char:
* typedef type_list<int, type_list<char> > test;
* test::value //int
* test::next::value //char
*/
template <typename T, typename S> struct type_list
{
typedef T value;
typedef S next;
};
/*
* Declaration of template struct for selecting a type from the list
*/
template <typename list, int b, int ctl> struct select_type;
/*
* Find a type with specified "b" bit in list "list"
*
*
*/
template <typename list, int b> struct find_type
{
private:
//Handy name for the type at the head of the list
typedef typename list::value cur_type;
//Number of bits of the type at the head
//CHANGE THIS (compile time) exp TO USE ANOTHER TYPE LEN COMPUTING
enum {cur_type_bits = numeric_limits<cur_type>::digits};
public:
//Select the type at the head if b == cur_type_bits else
//select_type call find_type with list::next
typedef typename select_type<list, b, cur_type_bits>::type type;
};
/*
* This is the specialization for empty list, return the null_type
* OVVERRIDE this struct to ADD CUSTOM BEHAVIOR for the TYPE NOT FOUND case
* (ie search for type with 17 bits on common archs)
*/
template <int b> struct find_type<null_type, b>
{
typedef null_type type;
};
/*
* Primary template for selecting the type at the head of the list if
* it matches the requested bits (b == ctl)
*
* If b == ctl the partial specified templated is evaluated so here we have
* b != ctl. We call find_type on the next element of the list
*/
template <typename list, int b, int ctl> struct select_type
{
typedef typename find_type<typename list::next, b>::type type;
};
/*
* This partial specified templated is used to select top type of a list
* it is called by find_type with the list of value (consumed at each call)
* the bits requested (b) and the current type (top type) length in bits
*
* We specialice the b == ctl case
*/
template <typename list, int b> struct select_type<list, b, b>
{
typedef typename list::value type;
};
/*
* These are the types list, to avoid possible ambiguity (some weird archs)
* we kept signed and unsigned separated
*/
#define UNSIGNED_TYPES type_list<unsigned char, \
type_list<unsigned short, \
type_list<unsigned int, \
type_list<unsigned long, \
type_list<unsigned long long, null_type> > > > >
#define SIGNED_TYPES type_list<signed char, \
type_list<signed short, \
type_list<signed int, \
type_list<signed long, \
type_list<signed long long, null_type> > > > >
/*
* These are acutally typedef used in programs.
*
* Nomenclature is [u]intN where u if present means unsigned, N is the
* number of bits in the integer
*
* find_type is used simply by giving first a type_list then the number of
* bits to search for.
*
* NB. Each type in the type list must had specified the template
* numeric_limits as it is used to compute the type len in (binary) digit.
*/
typedef find_type<UNSIGNED_TYPES, 8>::type uint8;
typedef find_type<UNSIGNED_TYPES, 16>::type uint16;
typedef find_type<UNSIGNED_TYPES, 32>::type uint32;
typedef find_type<UNSIGNED_TYPES, 64>::type uint64;
typedef find_type<SIGNED_TYPES, 7>::type int8;
typedef find_type<SIGNED_TYPES, 15>::type int16;
typedef find_type<SIGNED_TYPES, 31>::type int32;
typedef find_type<SIGNED_TYPES, 63>::type int64;
}
Существует стандарт, и он указан в различных документах стандартов (ISO, ANSI и еще что-то).
В Википедии есть отличная страница, объясняющая различные типы и максимальные значения, которые они могут хранить: Целое число в компьютерных науках.
Однако даже со стандартным компилятором C ++ вы можете легко найти следующий фрагмент кода:
#include <iostream>
#include <limits>
int main() {
// Change the template parameter to the various different types.
std::cout << std::numeric_limits<int>::max() << std::endl;
}
Документация для std :: numeric_limits можно найти в Roguewave . Он включает в себя множество других команд, которые вы можете вызвать, чтобы узнать различные пределы. Это можно использовать с любым произвольным типом, который передает размер, например std :: streamsize.
Ответ Джона содержит лучшее описание, которое гарантировано. Независимо от того, на какой платформе вы находитесь, есть еще одна хорошая страница, которая более подробно описывает, сколько бит каждый тип ДОЛЖЕН содержать: int types , которые определены в стандарте.
Надеюсь, это поможет!