IEEE 754-2008 - как представляются числа с плавающей точкой в современном компьютере (=> в C/C++)
Вольный пересказ
(стандарт IEEE-754 (не стандарт языка, стандарт железа, некоторые моменты перенял си))
Их всего 3 типа
float
= (32 бита) соответствует single precision IEEE 754-2008 10^(± 38)
double
= (64 бита) соответствует double precision IEEE 754-2008 10^(± 308)
long double
: (не договорились, про совместимость между компиляторами можно не думать)
может совпадать с double
может быть (128 бит) quad precision IEEE 754-2008, есть GCC, в x86 реализованно программно (очень медленно)
может быть (80 бит) extended precision (не входит в стандарт IEEE 754-2008, аппаратно реализован в x86, (быстрее любой программной реализации))
Диапазон в 10^(± 308) НЕ означает что будут храниться 308 десятичных знаков - означает, что максимальное значение по экспоненте будет 308 и оно будет неточным (будут храниться первые цифры)
Обычно мы не хотим использовать
long double
double
как минимум вдвое медленнее чем float
(может быть сильно медленнее)
Целые константы реализуются как int
, то есть 10
будет константной типа int
(можно подправить тип написав 10u
или 10llu
, тогда она будет unsigned int
и unsigned long long
соответсвенно)
Переполнение знакового (signed) int - Undefined Behavior
Дробные константы реализуются как double
, то есть 10.9
будет константной типа double
(можно подправить тип написав 10.9f тогда она будет float
)
Си всю арфметику считает в одинаковых типах! Нельзя к int прибавить unsigned. В случае, когда типы разные - копилятор их приводит к одному, например в случае int + unsigned int
победит unsigned int
float a = 5.7f; a += 5.6;
В этом случае a
сначала сконвертируется в double
сложится с константой и обратно сконвертируется в float
.
Можно создать 16ричные и 8ричные костанты в тексте, 16ричные константы с плавающей точкой. (0x43534P234
- 16ричное с точкой, после p экспанента)
Форматы ввода семейства функций printf/scanf
:
%i
- десятичные, также 16/8ричные с префиксом (0x/0) (int)%u
- десятичные беззнаковые (unsigned int)%x
- HEX (16рич), без префиксаФорматы вывода немного другие, в часности %i
и %d
с printf
работают одинакого, выводят десятичное число, чтобы вывести число в HEX надо использовать %x
Дробные:
%f
%lf
(сокр long float)Однако, если мы перпутаем в printf
- %f
и %lf
(скормим в %f
double или в %lf
float) то всё отработает хорошо, тк printf
- vararg
функция, а vararg
автоматически расширяет float
до double
. Это только в printf
. (типы которые короче int (например char) автоматически расширяются до int)
fopen
- открыть файл, принимает 2 аргумента
\\
, либо писать путь через прямые - /
"r"
- на чтение, если файла нет - вернет NULL."w"
- на запись, если файла нет - создает, если есть - обнуляет файл."a"
- на дозапись, если файла нет - создаст, если есть - откроет на конце файла.У режима есть модификаторы - t
или b
, текстовый и двоичный (бинарный) соответственно, применяется после режима - "rt"
, "wb"
, по умолчанию всегда текстовый режим.
Можно открыть на запись/чтение одновременно - добавить +
к режиму (до модификатора), например: r+b
или w+
, использовать очень аккуратно
Если происходит какая либо ошибка, нет файла/не удалось создать/нет прав - fopen
вернет NULL
, специальное значение (NULL == 0
). Рекомендуется всегда проверять, тк легко получить null pointer exeption - падение программы.
fclose
- закрыть файл, принимает указатель на FILE
(необходимо всегда закрывать файл, в противном случае, в лучшем случае, файл будет держаться до конца нашей программы (а возможно до перезагрузки компьютера))
Файлы обязательно надо закрывать! Желательно когда он перестал быть нужным, а не в конце программы. Тк этот файл блокируется пока он открыт нашей программой, так же под него выделенна память
Чтобы работать с файлами - мы можем использовать функции семейства printf/scanf, добавив f
к названию функции, и ещё один первый аргумент - указатель на файл (то, что возвращает fopen
)? напрмер: fscanf(in, "%i", &a);
В более низкоуровневых функциях (с - open
, win - create file
) можно указать какие операции мы хотим оставить другим пользователям.
C++ файловым (и не только) стандартным вводом и выводом лучше не пользоваться, тк под ними скрываются большие нетривиальные куски кода, которые медленно работают и плохо распаралеливаются (из-за глобальных переменных) (лучше в С++ использовать сишные функции ввода-вывода)
Модификатор b
двоичный - это про переводы строк
Какие бывают переводы строк:
0x0D 0x0A
(13 10) (\r
\n
)0x0A
(10) (\n
) (Linux) (new MacOC)0x0D
(13) (\r
)По ASCII, на самом деле:
0x0A
- Line Feed (перевод строки)0x0D
- Carriage Return (возврат каретки)
Когда мы пишем \n
в текстовом режиме - с run time
автоматически приобразует их в нужный перевод строки (под систему в которой мы сейчас), мы не хотим так в двоичном режиме (например, когда открываем картинку)
Если мы работаем с двоичными данными, мы всегда хотим открыть в режиме с модификатором b
. (часто это забывают сделать пользователи linux, тк у них совпадет \n
и системный перевод)
Стандартный ввод/вывод на самом деле тоже файловый, можно перенаправить из консоли в файл (
freeopen
)
Есть три стандартных потока:
- stdin - поток ввода
- stdout - поток вывода
- stderr - поток ошибок (пример -
fprintf(stderr, "Hi");
)
;
посреди текста - это пустой оператор, её любят ставить после }
, однако она не всегда там нужна и её не стоит пихать куда попало, например:
Так же можно создавать длинные цепочки if с помощью else if
- отрицает все предыдущие if и принимает новое утверждение в скобках, например:
If в скобках принимает логическое значение, то есть 0 или 1 (false или true), исполняет код под под if когда значение в скобках принимает логическую единицу, в связи с этим, и тем, что NULL ассоциируется с 0, например, можно короче записать проверку на неоткрытие файла:
рекомедуется писать
while
на строке с{
, для большей понятности
Эквивалент:
В таком эквиваленте есть только одно расхождение, при использовании слова continue;
continue;
- ключевое слово для цикла, означает пропустить всё до конца следующего шага (Оставляет С
в for
)
break;
- ключевое слово для цикла, означает выйти из цикла
Индекс в массиве - любая целочисленная переменная (значение)
Индексироваться double и float нельзя!\
Синтаксически можно управлять циклом (итерироваться) с помощью переменных с плавающей точкой, но так делать НЕ надо! Нпример, потому что число 1/10
непредставимо в двоичной системе, значение всегда будет чуть больше или чуть меньше, но никогда не 1/10
, то есть 1/10 * 10 != 1;
, более того умножить 1/10
и сложить 1/10
с высокой вероятностью тоже окажется не равным, тк в float и double постояноо происходят окугления. И это всё мы не хотим видеть при управление циклом!
Означает, что последний адрес в массиве 9! Чем больше программируешь - тем больше убеждаешься, что индексация с 0 - оочень удобно
Завести массив мы можем любого размера, однако надо помнить, что в 32 битных системах указатели размерности 32 бита, то есть ~4 миллиарда значений (байтиков, 4 гб), и мы не сможем создать массив больше 4х миллиардов, тк просто не хватить указателей (в 64 битных системах количество указателей в 4млрд раз больше - фактически, гораздо больше чем можно поставить оператичной памяти в компьютер)
Когда мы обращаемся по индексам си НЕ проверяет, что мы уместились в диапазон, например
Потому что мы пишем в рандомную ячейку памяти, она может хранить всё что угодно: другая переменная, служебные переменные, даже кусок кода => здесь может ничего не произойти, а могут дикие спец эффекты
(стандарт си не гарантирует ничего в этом случае)
Одна из сложностей языка "C" - нужно следить что ты не вышел за границы массива.
Сложность в том, что когда мы бажим в Undefine believer, нам стандарт ничего не обещает, полный undefine, по этому у нас программа может работать правильно, а у пользователя, у другого программиста или просто на том же компьютере в другое время - отработать неправильно или вообще упасть.
q[z][y][x]
принято так индексировать многомерные массивы, тк это больше всего похоже на то, как они храняться в памяти
Размеры связанны с тем, где мы создаем переменные. Если мы создаем массив как обычную локальную переменную - общее ограничение на размер стека (пара мегабайт для каждой программы). Если мы попытаемся выделить массив больше, либо программа скажет нам stack overflow, либо просто тихо исчезнет, тк чтобы что-то сказать (сделать) программе нужно свободное место на стеке
stackoverflow.com - одноименный шикарный сайт (форум), вопросов и ответов на темы программирования.
Full - C/C++, 3 лекция, 26.02.2022, Скаков