Double or Nothing / Хабр

Если вы выбрали C++ в качестве языка программирования, то учить его придётся всю жизнь. Смиритесь. Или выбирайте другой язык.

Чего только стоят новые стандарты, появляющиеся каждые 3 года. И каждый раз с какими-то полезными нововведениями в синтаксисе! Как им только это удаётся?

Есть разные способы совершенствоваться в С++. Кто-то читает Страуструпа от корки до корки, а потом почитывает стандарты. Кто-то ничего не читает и программирует по наитию — тупиковый путь, на мой взгляд.

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

Одна из моих любимых таких вещей — Guru of the Week (GotW) Херба Саттера.

Написано остроумно. И сколько пользы для программистки! Некоторые статьи уже морально устарели: кому нужен auto_ptr в наши дни? Но большинство ценно и сегодня.

Приведу здесь перевод выпуска №67 «Double or Nothing» от 29 февраля 2000 года. Моё любимое место — про тепловую смерть конечно ​

«Сложность 4/10

Нет. Этот выпуск не об азартных играх. Он, впрочем, о разных видах «float» так сказать, и даёт вам возможность проверить навыки касающиеся базовых операций над числами с плавающей точкой в C и C++.

Проблема

Вопрос Йуного Гуру (JG)

1) В чём разница между «float» и «double»?

Вопрос Гуру

2) Допустим следующая программа выполняется за 1 секунду, что неудивительно для современного настольного компьютера:

int main() {

double x = 1e8;

while (x > 0) { --x; }

return 0;

}

Как долго по-вашему она будет выполняться, если заменить «double» на «float»? Почему?

Решение

1) В чём разница между «float» и «double»?

Цитата из секции 3.9.1/8 стандарта C++:

Существует три типа чисел с плавающей точкой: float, double и long double. Тип double предоставляет по меньшей мере такую же точность как float, а тип long double — по меньшей мере такую же точность как double. Множество значений типа float является подмножеством множества значений типа double; множество значений типа double является подмножеством множества значений типа long double.

Колесо Времени

2) Как долго по вашему программа из вопроса 2 будет выполняться, если заменить «double» на «float»? Почему?

Она будет выполняться или примерно 1 секунду (в конкретных реализациях float’ы могут быть быстрее, такими же быстрыми или медленнее, чем double’ы) или бесконечно, в зависимости от того может или нет тип float представлять все целые числа от 0 до 1e8 включительно.

Цитата из стандарта выше означает, что могут существовать такие значения, которые могут быть представлены типом double, но не могут быть представлены типом float. В частности, на многих популярных платформах и во многих компиляторах double может точно представить все целые числа в диапазоне [0, 1e8], а float — не может.

Что если float не может точно представить все целые числа в диапазоне от 0 до 1e8? Тогда изменённая программа начнёт обратный отсчёт, но в конце концов достигнет значения N, которое не может быть представлено и для которого N-1==N (из-за недостаточной точности чисел с плавающей точкой) … и тогда цикл застрянет на этом значении пока машина, на которой запущена программа не разрядится (из-за проблем в локальной энергосети или ограничений батареи), её операционная система не сломается (некоторые платформы более подвержены этому, чем другие), Солнце не превратится в переменную звезду и спалит внутренние планеты или вселенная не погибнет тепловой смертью — любое, что произойдёт раньше*.

Слово о Сужающих Преобразованиях

Некоторые люди могут интересоваться: «Хорошо, кроме всеобщей тепловой смерти нет ли здесь другой проблемы? Константа 1e8 имеет тип double. Тогда если мы просто заменим «double» на «float», программа не скомпилируется по причине сужающего преобразования, верно?»

Хорошо, давайте процитируем стандарт ещё раз, на этот раз секцию 4. 8/1:

rvalue [выражение справа от знака «=» — прим. переводчицы] типа с плавающей точкой может быть преобразовано к rvalue другого типа с плавающей точкой. Если исходное значение может быть точно представлено целевым типом данных, результатом преобразования будет являться то точное представление. Если исходное значение лежит между двух последовательных значений целевого типа данных, результат преобразования определяется реализацией в качестве одного из этих двух значений. Иначе поведение не определено.

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

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

Примечания

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

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

(c) Херб Саттер, GotW

Основные типы данных в С++

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

Вот основные типы данных:

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

Итак рассмотрим поподробнее каждый из типов.

Int.

Самым простым и главным является тип int.

Он используется для хранения целых чисел. В переменную типа int можно записать, например, числа -2, 0, 5. И вообще любые целые числа из его диапазона. Если в переменную типа int попытаться записать дробное число, то компилятор автоматически отбросит дробную часть и в переменной останется только целая часть(произойдет именно отбрасывание, а не округление по правилам математики). То есть записав в int числа 12.

25 и 12.94, в обоих случаях получится целое число 12. Также в тип int можно записать букву, тогда в переменной будет код этой буквы.

int a;
a = 12;
cout << a << » «;
a = 12.25;
cout << a << » «;
a = 12.94;
cout << a << » «;
a = ‘d’;
cout << a;

Как итог получим:

12 12 12 100

Long long.

Тип long long представляет из себя то же самое, что и int, только имеет более широкий диапазон(что очень ярко отражено в названии типа).

Double.

При помощи типа double мы можем хранить вещественные(дробные) числа. Например в тип double можно записать число 39.1804. Кроме того в double можно записать и любое целое число, тогда дробная часть будет представлять из себя 0.

Char.

С помощью типа char мы можем хранить какой-нибудь символ.

На самом деле в типе char хранится не сам символ, а код этого символа. Поэтому, если мы запишем в char какое-либо целое число, то получим символ с этим кодом.

char c;
c = ‘Q’;
cout << c << » «;
c = 100;
cout << c;

Q d

Наверняка может возникнуть вопрос, как диапазон числа связан с занимаемым им размером. Дело в том, что любая переменная хранится в компьютере в двоичном представлении и каждый бит — это какой-то разряд числа. Вот например тип int занимает 4 байта или 32 бита. В первом бите хранится знак числа, так что остается 31 рабочий бит. То есть с его помощью можно хранить число, двоичное представление которого содержит 31 разряд. А при помощи 31 разряда можно хранить любые числа от 0 до 2

31(примерно 109). Плюс еще один бит, который содержит знак и мы получаем исходный диапазон -231…231.

Примечание.

Во многих языках программирования существует отдельный тип для хранения строк string. В С++ нет отдельного типа данных для строк. Вместо этого существует специальный контейнер string, использование которого намного удобнее и функциональнее, нежели использование простого типа данных.

Однако о различных контейнерах будет рассказано позже в рубрике STL.

Понимание типов данных C — char, int, float, double и void

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

Например, вы можете использовать число , например 1 , 2 , 100 , или число с десятичной точкой , например 99.95 , 10.5 или текст , например «Studytonight» , все эти значения по-разному обрабатываются компилятором языка C , следовательно, мы используем типы данных для определения типа данных, используемых в любом программа.

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

Типы данных в C

В общем, существует 5 различных категорий типов данных в языке C, а именно:

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

Первичные типы данных в C

Язык C имеет 5 основных (первичных или примитивных) типов данных:

  1. Символ: Мы используем ключевое слово char для символьного типа данных. Он используется для хранения однобитовых символов и занимает 1 байт памяти. Мы можем хранить алфавиты от AZ (и az) и цифры 0-9, используя char . Например,

     символ а = 'а';
    символ б = 'А';
    символ с = '0';
    символ д = 0; //ошибка 

    Для типа данных

    char необходимо заключить наши данные в одинарных кавычках . Мы можем выполнять операции сложения и вычитания над символами , но значение ASCII не должно превышать 127.

  2. Целое число: Мы используем ключевое слово int для целочисленного типа данных. Тип данных int используется для хранения не дробных чисел, которые включают положительные, отрицательные и нулевые значения. Диапазон int равен -2 147 483 648 до 2 147 483 647 и занимает 2 или 4 байта памяти, в зависимости от используемой системы. Например,

     int a = 5550;
    интервал b = -90,
    интервал с = 0;
    инт д = -0,5; //invalid 

    Мы можем выполнять сложение, вычитание, деление, умножение, побитовые операции и операции по модулю с типом данных

    int .

  3. С плавающей запятой: Мы используем ключевое слово float для типа данных с плавающей запятой. float используется для хранения десятичных чисел . Он занимает 4 байта памяти и находится в диапазоне от 1e-37 до 1e+37 . Например,

     с плавающей запятой a = 0,05;
    поплавок b = -0,005.
    поплавок с = 1; // оно станет c = 1.000000 из-за приведения типов
     

    Мы можем выполнять операции сложения, вычитания, деления и умножения над типом данных

    с плавающей запятой .

  4. Double: Мы используем ключевое слово double для двойного типа данных. двойной используется для хранения десятичных чисел . Он занимает 8 байт памяти и находится в диапазоне от 1e-37 до 1e+37 .

     двойной а = 10,09;
    двойной б = -67,9; 

    double имеет большую точность, чем flaot , поэтому double дает более точные результаты по сравнению с float .

    Мы можем выполнять операции сложения, вычитания, деления и умножения с типом данных double .

  5. Недействительно: Это означает отсутствие значения. Этот тип данных в основном используется, когда мы определяем функции. 9Тип данных 0087 void используется, когда функция ничего не возвращает. Он занимает 0 байт памяти. Мы используем ключевое слово void для типа данных void.

     недействительная функция () {
       //здесь будет ваш код
    } 

Каждый тип данных имеет размер , определенный в битах/байтах , и имеет диапазон

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

6 Типы данных в статистике и…

Включите JavaScript

6 Типы данных в статистике и исследованиях: Key in Data Science

Размер различных типов данных в C

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

char равен 1 байту

Тип данных char имеет размер 1 байт или 8 бит . Это в основном то же самое и не зависит от используемого процессора или компилятора.

int может быть 2 байта/4 байта

Существует очень простой способ запомнить размер для типа данных int . Размер типа данных int обычно равен длине слова среды выполнения программы. Проще говоря, для 16-битной среды , int равно 16 бит или 2 байта , а для 32-битной среды , int равно 32 бита или 4 байта .

число с плавающей запятой равно 4 байтам

Тип данных float имеет размер 4 байта или 32 бита . Это тип данных одинарной точности , который используется для хранения десятичных значений. Он используется для хранения больших значений.

float является более быстрым типом данных по сравнению с double , потому что тип данных double работает с очень большими значениями, следовательно, он медленный.

double — 8 байт

Тип данных double 8 байт или 64 бита размером . Он может хранить значения, которые в раза удваивают размер того, что тип данных float может хранить , поэтому он называется double .

В 64 битах, 1 бит для представления знака , 11 бит для экспоненты , а остальные 52 бита используются для мантиссы 9 0004 .

Тип данных double может содержать примерно от 15 до 17 цифр , до и после запятой.

void равен 0 байтам

Тип данных void ничего не значит, поэтому он не имеет размера.

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

Модификаторы типа данных C:

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

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

Подобным образом в языке C есть модификаторы , которые делают первичные типы данных более конкретными .

Ниже приведены модификаторы:

  1. подписанный

  2. беззнаковый

  3. длинный

  4. короткий

Как следует из названия, со знаком и без знака используются для представления со знаком (+ и -) и беззнаковые (только +) значения для любого типа данных. И long и short влияют на диапазон значений для любого типа данных.

Например, signed int , unsigned int , short int , long int и т. д. — все допустимые типы данных в языке C.

 длинное длинное число = 123456789987654321; // мы не можем сохранить такое большое значение, используя тип данных int. 

Теперь посмотрим диапазон для разных типов данных, сформированный в результате 5 первичных типов данных вместе с указанными выше модификаторами.

C Тип данных Диапазон значений

В таблице ниже указаны диапазоны для различных типов данных в языке C.

900 41
Тип Типовой размер в битах Минимальный диапазон Спецификатор формата
char 8 -127 до 127 %c
без знака символ 8 от 0 до 255 %c
подписанный символ 8 -127 до 127 900 87 %c
целое 16 или 32 от -32 767 до 32 767 %d , %i
без знака int 16 или 32 от 0 до 65 ,535 %u
подписанный целый 16 или 32 То же, что и int %d , %i
короткий int 16 от -32 767 до 32 767 %hd
беззнаковый короткий целое число 16 от 0 до 65 535 %hu
подписанный короткий целый 16 То же, что и короткий int %hd
long int 32 от -2 147 483 647 до 2 147 483 647 %ld , %li
длинный long int 64 -(2 63 — 1) to 2 63 — 1 (добавлено стандартом C99) %lld , %lli
signed long int 32 То же, что и long int %ld , %li
без знака long int 32 от 0 до 4 294 967 295 %lu
без знака long long int 64 2 64 — 1 (добавлен стандартом C99) %llu
float 32 От 1E-37 до 1E+37 с точностью до шести цифр 9008 7 %f
двойной 64 1E-37 до 1E+37 с точностью до десяти цифр %lf
long double 80 1E- от 37 до 1E+37 с точностью до десяти цифр %Lf

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

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

Что произойдет, если значение выйдет за пределы допустимого диапазона?

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

Код запуска →

Когда модификатор типа используется без какого-либо типа данных , тип данных int устанавливается в качестве типа данных по умолчанию. Итак, unsigned означает unsigned int , signed означает signed int , long означает long 90 087 int и short означает short int .

Что означает

со знаком и без знака значит?

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

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

Когда компилятор получает числовое значение, он преобразует это значение в двоичное число, что означает комбинацию 0 и 1. Например, 32767 в двоичном виде равно 01111111 11111111 и 1 в двоичном формате — это 01 (или 0001) , 2 — это 0010 и так далее.

В случае целого числа со знаком бит старшего разряда или первая цифра слева (в двоичном формате) используется как флаг знака . Если флаг знака равен 0 , число положительное , а если 1 , число отрицательное .

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

Для со знаком int , 11111111 11111111 означает -32 767 и поскольку первый бит является флагом знака , чтобы пометить его как отрицательное число, а остальные представляют число. Тогда как в случае unsigned int , 11111111 11111111 означает 65,535 .

Производные типы данных в C

Хотя существует 5 основных типов данных, в языке C также есть несколько производных типов данных, которые используются для хранения сложных данных.

Производные типы данных — это не что иное, как первичные типы данных, но немного искаженные или сгруппированные вместе, такие как массив , структура , объединение , и указатели . Они подробно обсуждаются позже.

Заключение:

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


  • ← Предыдущая
  • Следующая →

Понимание переменных Float и Double в C

Основные понятия C

В этом уроке мы изучим основы переменных Float и Double в программировании на C и чем они отличаются по точности и емкости для хранения десятичных чисел.

Переменные типа Float и Double

Как описано выше, целочисленное значение переменной не имеет дробной (десятичной) дроби. часть. C использует два типа переменных float и double , которые могут хранить десятичное число. Каждый из них предлагает различный уровень точности (способность хранить числа после запятой), как указано ниже.

  • поплавок — Переменная типа данных с плавающей запятой может хранить не более 6 цифр точности. Объем памяти типа данных float составляет 4 байта и может хранить число в диапазоне от 1.2E-38 до 3.4E+38.
  • двойной — Переменная типа данных double может хранить до 15 цифр точности. Объем памяти типа данных double составляет 8 байт и может хранить число в диапазоне от 2.3E-308 до 1.7E+308.

Примечание: Основное различие между типами данных float и double заключается в их объеме памяти, точности и диапазоне чисел, как указано выше.

Синтаксис объявления переменных типа float и double в C

 float variable_name;
двойное имя_переменной; 

Здесь float используется для объявления типа данных Float и

double используется для объявления типа данных Double . имя_переменной имя переменная (можно использовать любое имя по вашему выбору, например: a, b, c, alpha и т. д.) и ; используется для обозначения конца строки (конец строки).

Теперь давайте рассмотрим несколько примеров для лучшего понимания.

Пример 1

Объявить переменную float x и переменную double y

 float x;
двойной у; 
Пример 2

Объявите переменную с плавающей запятой x для присвоения десятичного числа 18,286 и двойная переменная y для присвоения десятичного числа 16722.

02675585.

 с плавающей запятой х = 18,286;
двойной у = 16722,02675585; 
Пример 3

Объявить 3 переменные типа float a , b , c и 3 double переменная x , y , z

 float a, b, c;
двойной х, у, г; 
Пример 4

Объявить 3 переменные с плавающей запятой a , b и c для назначения значение с плавающей запятой 41,23, 87,335 и 62,471 соответственно после этого объявления 3 double переменные x , y и z для назначения двойное значение 190,2347, 22,20 и 54,458691027 соответственно

 с плавающей запятой a=41,23, b=87,335, c=62,471;
двойной x=19,2347, y=22,20, z=54,458691027; 
Пример 5

Объявить переменную float x и двойную переменную y и присвойте значение с плавающей запятой 12,365 и двойное значение 79,2358 отдельной строкой соответственно.

 с плавающей запятой х;
двойной у;
х = 12,365;
у = 79,2358; 
Пример 6

Объявление переменной float x и переменной double у и назначьте значение float 7,18 и двойное значение 11,62254 после этого измените их значения на 44,23 и 67,48612 соответственно.