CodeLab
145 subscribers
538 photos
20 videos
159 links
Говорим просто о сложном

Обсуждение, новости, материал и сплетни — все здесь https://t.me/CodeLabMLChat
Download Telegram
Завтра попробую об этом поподробнее рассказать, всем сладких 😋
👨‍💻 Иииитак, вчера я ничего не делал, щас буду рассказывать про хранение памяти в компьютере, как раз это свяжем с проблемой чисел с плавающей точкой, поэтому постарайтесь все внимательно прочитать‼️

⭕️ Для хранения одной ячейки информации в памяти используется байт (8 битов), а не один бит. Как лично я понял, когда идет речь об обращении к 'ячейке памяти', то речь тут все-таки о байтах.

0️⃣ В одном байте, как мы знаем 8 бит, но процессор обращается к памяти на уровне байтов. Он может иметь значение 1 или 0. следовательно 1 будет означать передачу тока в транзистор, а 0 – нет. ⭕️ Каждая такая ячейка имеет уникальный адрес, который используется для доступа к данным, хранящимся в этой ячейке. А нужно это для быстрого поиска и использования информации, процессор будет напрямую обращаться к любой ячейке памяти.

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

1️⃣ Информация дальше подпизжена частично из GPT (просто на всякий вам): Значит, есть формат IEEE 754, который описывает числа с плавающей точкой (лично я это не знал, поэтому пишу вам).

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

2️⃣ Порядок
(экспонента) — отвечает за масштаб числа, сдвигая десятичную точку.

3️⃣ Мантисса
— содержит значимые цифры числа. Теперь об этом поподробнее. Я буду ссылаться на сайт https://struchkov.dev/blog/ru/floating-point-math/:

4️⃣ Итак, возьмем вещественное число 6.25 и переведем его в двоичный код. (Хочу показать как найти мантиссу)

⭕️ Целую часть (6) мы делим на 2, при этом записывая остаток от деления:
6 : 2 = 3[0], 3 : 2 = 1[1], 1 : 2 = 1[1]❗️ (В квадратных скобках остаток от деления). (И получили мы 110, т.к с конца записали)

Дробную часть (0.25) в свою очередь умножаем на 2, при этом, целую часть записываем, а дробную продолжаем умножать: 0.25 * 2 = 0.5 (0 записали, а 0.5 продолжаем умножать на 2, получаем 1 (0.5 x 2 = 1.0), единицу тоже записываем (Записываем не с конца, просто получаем на выходе 01).

5️⃣🔗Теперь, все как в школе🧑‍💻: остатки от деления целой частей записываем с конца - 110, и дробную, также с конца - получаем 01. Соединяем и получаем 110.01, это наше число 6.25 только в двоичном виде. Если вам не понятен перевод числа, я прикреплю фотки после сообщения, это будет [1] изображение. Так что это за ебанная мантисса вообще?

‼️ Ниже будет разбор по фотке, чтобы было понятнее ‼️
‼️ Мантисса (или дробная часть) — это часть числа, которая определяет его точность. Я уже упомянул стандарт IEEE-754, согласно этому стандарту, двоичное число представляется в виде формулы, где s — знак числа, M — мантисса, B — основание, E — экспонента (порядок, степень двойки).

⭕️ Поскольку мы работаем в двоичном коде, основание равно двум, и формула принимает следующий вид: [2]. Вещественное число мы сохраняем как три отдельных числа: знак, экспоненту и мантиссу, да и все бы заебись, но количество бит для хранения ограниченно форматом.

👨‍💻Формат если че это такая структура, которая описывает, как именно должны быть организованны данные. ❗️Именно формат определяет, сколько битов выделяется для различных частей данных: для мантиссы, знака и порядка(экспоненты).

0️⃣ Рассматриваем формат, в котором доступно 32 бита: 1 бит знака, 8 битов для экспоненты и 23 бита для мантиссы. Этот формат называется ‼️"одинарной точностью". Также есть формат 'Двойная точность (64 бита)': 1 бит для знака, 11 битов для экспоненты, 52 бита для мантиссы.

⁉️Дак вот, нам нужно сдвинуть запятую нашего числа с прошлого поста 110.01 влево настолько, пока в целой части не останется единица. При этом, каждый сдвиг увеличивает степень основания (сейчас поясню).

1️⃣ На первом этапе у нас число 110.01 * 10 ** 0, все написано по формуле! Десятка в основании, так как это 2 в двоичной степени, сам автор статьи это поясняет. Следующим этапом будет сдвиг запятой влево, т.е получится 11.001 * 10 ** 1.

2️⃣Ну и последним этапом, 1.1001 * 10 ** 2. В итоге мы получаем
1.1001 x 2 **
2. (По сути просто сдвигаем влево и увеличиваем степень нашей '2')

Здесь 1.1001 - это наша мантисса, а поскольку наше число положительное, бит знака будет равен 0.

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

‼️Чтобы дальше получить экспоненту, нам нужно прибавить число 127
. То есть было у нас число 1.1001 * 2 ** 2, к этой двойке прибавляем 127, с каких это х‼️ев?

🔗Под экспоненту в нашем представлении чисел с плавающей запятой выделено 8 бит, что позволяет сохранить 256 различных значений. Но есть проблемка, ведь в нашем представлении не предусмотрено отдельного бита для знака экспоненты.

‼️Решением проблемы будет состоять в хранении экспоненты относительно середины доступного диапазона значений, то есть диапазон чисел от 0 до 255, а для 8-битного диапазона этой "серединой" будет число 127 [3] изображение.

3️⃣ По итогу, к нашей изначальной степени 2 прибавляем 127 и получаем 129. При преобразовании 129 в двоичную получаем 10000001 - это и будет экспонента. Зачем вообще это все нужно ?

Ответ - да х🌶й знает вообще, мне лично стало интересно почитать и изучить, как это работает в действительности, а не просто 'а нуу, 0.1 + 0.1 не равно 0.2 да и ладно, написано, значит написано'. Спасибо большое автору статьи! Сейчас скину пару изображений и перейдем к финалу
⭕️ Согласно стандарту, формула для представления двоичного числа
⭕️ Поскольку преобразования происходят в двоичном коде, основание экспоненты равно 2
Формат "одинарной точности", в котором доступно 32 бита: 1 бит знака, 8 битов для экспоненты и 23 бита для мантиссы.
🔗Еще, было указано про прием, который применяли еще в первых машинах, который позволял не сохранять целую часть числа, так как она всегда равна единице. Это выглядит вот так, поэтому целую часть мы не записываем, она всегда равна единице.
Нормализованное число (нормисное) имеет следующий вид: (Просто добавляем единицу)
⭕️ 8 битный диапазон, для нахождения экспоненты. Все отрицательные степени будут располагаться левее числа 127, а все положительные - правее.
🧑‍💻 Конечный результат
👨‍💻2
‼️Все что было описано выше 👆🏿 это процесс перевода вещественного числа в двоичное представление.

Опять же, это применимо с удачными числами, без десятичных дробей 0.1, 02, 0.3 - 0.9
👨‍💻2
👨‍💻2
Теперь, разберемся наконец с этими вещественными числами.

Почему 0.1 + 0.1 не будет равно точно 0.2

⭕️Число 0.1 в двоичной системе счисления будет равно 0.00011001100... и так далее, это бесконечная периодическая дробь в двоичной системе. Также как числа 0.1, 0.2 вплоть до 0.9, кроме 0.5 (Дальше узнаете почему).

Так вот проблема в том, что у нас память ограничена 32 битами, а представление числа в двоичной системе счисления бесконечно‼️

⭕️Это возможно сделать только путем отсечения той части числа, которая не помещается в 32 бита. По итогу, это число обрезается и представляется приближённым значением
, к примеру,

0.1+0.1=0.10000000000000000555...+0.10000000000000000555...


🔗Сумма этой темы будет равна примерно 0.2≈0.20000000000000001110...

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

🧑‍💻Хочу еще добавить, что, когда я кидал промт GPT,он аргументировал неточность вычислений еще тем, что вещественные числа не могут быть точно представлены в двоичной системе счисления, если они не могут быть выражены как конечная сумма степеней двойки. Че это ваще значит?

‼️Если возьмем 0.5, то все знают, что это 1/2, а если это одна вторая, то двойку можно представить как 2 в степени -1
. Точно также мы можем выразить степени двойки у чисел 0.625, 0.75 и т.д Если что-то еще найду, обязательно добавлю, спасибо еще раз автору статьи и вам за внимание! #Алгоритмы #Вещественные_числа #Программирование Вещественные числа
👨‍💻2
‼️Хочу немного добавить к вышесказанному👆🏿

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

Экспонента
(или порядок) определяет величину числа, указывая, на сколько нужно сдвинуть запятую в мантиссе для получения полного числа. Вот вам число 2.997925 · 10^8 = 299 792 500, экспонента здесь 8, и я уже объяснил, почему (потому что 8 сдвигов было). Жирным выделена мантисса (2.997925), а курсивом экспонента (8). ‼️Число = Мантисса × 2 ** Экспонента ‼️- это формула представления числа с плавающей точкой, это мы с вами уже обсуждали.

Допустим, мы хотим умножить два числа с плавающей точкой: Мантиссы обоих чисел перемножаются, что даёт новую мантиссу - это первое, Порядки (экспоненты) складываются - это второе. Щас мем смешной скину и пойду спать, а у нас впереди тема массивов и связанных списков... #Мантисса
👨‍💻2
🤡3
Следующее задание #Дробная_часть, как раз тут нам поясняют проблему с числами типа float ,❗️ там чел оставил ссылку почитать, я вам ее прикреплю, но прям сильно другое там не написано, вчера я достаточно нормально все изъяснил.

Сейчас решим задачку и я вам расскажу как можно избежать проблем с расчетом таких чисел

‼️https://docs-python.ru/tutorial/operatsii-chislami-python/problemy-chisel-plavajuschej-zapjatoj/#float-error‼️
👨‍💻2
Самое простое решение, из вещественного числа вычесть целую часть числа, 44.45 - 45 = 0.45.

a = float(input())
print(a - int(a))
👨‍💻2