Системы представления чисел Если вы всерьез собрались научиться разрабатывать микропроцессорные устройства, тогда вам необходимо знать, как компьютер хранит и обрабатывает числа. К сожалению люди не придумали способа записывать при помощи электронных сигналов числа в том виде как мы их привыкли видеть (вернее пытались, но попытки закончились неудачно), поэтому матемaтикам и инженерам пришлось придумать другой, более подходящий способ. Система счисления ― это совокупность приемов представления обозначения натуральных чисел. В обычной жизни используются два способа представления чисел ― при помощи индийских и римских цифр. Одно и тоже число можно записать как 2 или как II, как 5 или как V, как 22 или как XXII. Перед нами два альтернативных способа написания чисел. Числа одинаковые, а способы написания разные. Если есть два способа, почему бы не придумать третий, четвертый и далее? Были придуманы несколько способов представления чисел. Один из этих способов (двоичный способ представления чисел) подходит для реализации его при помощи электроники. Другие (восьмеричный и шестнадцатеричный) способы помогают удобнее представлять компьютерные данные на бумаге или на экране. За основу универсального приема, позволяющего создавать различные способы представления чисел были взяты индийские цифры и позиционный способ представления числа. Согласно этому принципу один и тот же цифровой знак имеет различные значения в зависимости от того места (позиции), где он расположен. Такая система счисления основывается на том, что некоторое число из n единиц (основание системы счисления) объединяется в единицу второго разряда, n единиц второго разряда объединяются в единицу третьего разряда и так далее. «Разрядам» учат в начальных классах школы. Например, у числа 35672 цифра «2» соответствует первому разряду, «7» ― второму, «6» ― третьему, «5» ― четвертому и «3» ― пятому. А «различные значения» цифрового знака «в зависимости от того места, где он расположен» и «объединение в единицу старшего разряда» очень наглядно отображают обыкновенные бухгалтерские счеты. Чтобы набрать число 35672 мы должны передвинуть влево две «костяшки» на первом «прутике», 7 на втором, 6 на третьем, 5 на четвертом и 3 на пятом. (1 «костяшка» на втором «прутике» ― это то же самое, что и 10 «костяшек» ― на первом, а одна на третьем равна десяти на втором ― и так далее...) Пронумеруем наши «прутики» снизу вверх ― так, чтобы номер первого «прутика» был равен нулю. 35672 = 3⋅104 + 5⋅103+ 6⋅102 + 7⋅101 +2⋅100 → сколько на каждом «прутике» сдвинутых влево «костяшек»; 35672 = 3⋅104 + 5⋅103+ 6⋅102 + 7⋅101+ 2⋅100 → номер «прутика»; 35672 = 3⋅104 + 5⋅103+ 6⋅102 + 7⋅101 +2⋅100 → число «костяшек» на каждом «прутике». Оттолкнувшись от записи конкретного числа 35672 можно записать любое пятизначное число как a4a3a2a1a0=a4×104+a3×103+a2×102+a1×10+a0где a4, a3, a2, a1 и a0 ― какие-то десятичные цифры, а число 10 ― основание системы представления. Компактный способ написания разложения (n+1)-значного числа в линейную комбинацию степеней базы выглядит вот так: anan-1...a2a1a0=[math]\displaystyle \sum^{n+1}_{k=0}[/math]ak×10kВ большинстве стран мира считают одинаково, используя десять цифр (0, 1, 2, 3, 4, 5, 6, 7, 8, 9). Когда доходят до последней цифры, используют двухзначные числа. Когда заканчиваются двухзначные числа ― начинают использовать трехзначные и так далее... Число десять выбрано потому, что у нас на двух руках десять пальцев Если бы у человека было на каждой руке только по четыре пальца, как у героев диснеевских мультфильмов (рис. #1), тогда бы ему никогда не пришла в голову мысль о системе счисления, основанной на десяти. Вместо нее нормальной и единственно разумной системой счисления считалась бы система, основанная на восьми пальцах. И называлась бы такая система не десятичной (decimal), а восьмеричной (octal). В восьмеричной системе счисления не нужен символ 9. В десятичной системе нет специального символа для десяти, поэтому в восьмеричной системе нет специального символа для 8. В десятичной системе начало натурального ряда чисел выглядит так: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... В восьмеричной системе уже так: 0, 1, 2, 3, 4, 5, 6, 7 и... что дальше? Символы-то закончились. Выход один ― после 7 поставить 10. Но эта десятка уже не равна числу пальцев на двух руках человека. В восьмеричной системе 10 ― это количество пальцев на двух руках мультяшки. Даже когда пальцы на ногах и руках будут исчерпаны, счет в восьмеричной системе можно продолжать. В целом он идет, как и счет в десятичной системе, но без чисел, содержащих цифры 8 и 9: 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21, 21, 22, 23, 24, 25, 26, 27, 30, 31, 31, 32, 33, 34, 35, 36, 37, 40, 41, 41, 42, 43, 44, 45, 46, 47, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 63, 64, 65, 66, 67, 70, 71, 72, 73, 74, 75, 76, 77, 100... «Дети шпионов». 2001. Пал ПалычиА теперь представьте себе как считают вот такие существа (рис. #2). На обоих руках у них всего два пальца, поэтому обходятся они всего двумя цифрами: 0 и 1. Система счисления, основанная на двух цифрах, называется двоичной (binary). В двоичной системе сразу после 1 идет 10. В любой системе счисления, когда заканчиваются одноразрядные числа, первое двухразрядное число ― 10. Начало натурального ряда чисел в двоичной системе: 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111, 10000, 10001... Закрепим. Если бы у нас было только пять цифр, 0, 1, 2, 3, 4, тогда начало натурального ряда чисел выглядело бы так: 0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31, 32, 33, 34, 40, 41, 42, 43, 44, 100, 101, 102, 103, 104, 110, 111, 112... «Обычную» систему представления чисел называют «десятичной». Систему представления чисел, которую мы только что рассмотрели ― пятеричной. Число цифр в системе представления чисел (количество пальцев на двух руках) называют «основанием системы представления чисел» или базой. Если основании системы более десяти, тогда приходится вводить новые «цифры». В качестве «новых цифр» принято использовать латинские буквы. Так, в семнадцатеричной системе представления начало натурального ряда чисел выглядит вот так: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, G, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 1G, 20, ..., GD, GE, GF, GG, 100, 101, 102, ... Задание #1: При каком достаточно большом основании способ с использованием латинских букв не подойдет? Разные системы представления чисел ― как разные языки. У одного и того же числа могут быть совершенно разные записи в разных системах представления. Так, десятичному представлению числа 2785 соответствует шестнадцатеричное представление AE1, восмеричное 5341 и двоичное 101011100001. Точно так же, как слова переводят с одного языка на другой, числа можно переводить из одной системы представления в другую. Двоичная система представления чисел используется в компьютерах. К недостатку двоичной системы представления можно отнести «длинную» запись числа (чем меньше в системе представления цифр, тем длиннее будет представление числа) ― четырем цифрам в десятичном представлении числа 2785 соответствует двенадцать цифр в двоичном представлении 101011100001 Шестнадцатеричная (hexadecimal) и восьмеричная системы представления чисел позволяют вместо длинных цепочек из нулей и единиц записывать более «короткие» числа, что облегчает написание и корректировку программ. Шестнадцатеричная и восьмеричная системы ― промежуточное звено между машиной и человеком, между двоичной и десятичной системами представления чисел. Разумеется, что нули перед числом не изменяют его значение ни в одной из систем представления. Задание #2: Посчитайте немного в различных системах представления. Возьмите листочек «в клеточку» и пронумеруете строчки сначала в десятичной системе представления, потом в восьмеричной, шестнадцатеричной и двоичной ― получите табличку перевода первых тридцати чисел в наиболее часто используемых системах представления. Напишите на этой таблице также числовые значения всех латинских букв ― это пригодится в дальнейшем.
Для представления бинарных файлов в текстах писем в электронной почте используется 6-ричное кодирование base64, которое при помощи 64(=26) символов (текстово-цифровые латинские символы A-Z, a-z и 0-9 (62 знака) и 2 дополнительных символа, зависящих от системы реализации) Или вообще попробуйте считать в 256-ричной (=28) системе счисления, используя в качестве «цифр» таблицу ASCII-символов Компьютер, как известно, считает только в двоичной системе счисления. Человеку привычна десятичная. Так для чего еще и шестнадцатеричную какую-то знать нужно? Все очень просто. В умных книжках пишут, что «шестнадцатеричная нотация является удобной формой представления двоичных чисел». Что это значит? Переведите число A23F из шестнадцатеричной «нотации» в двоичную. (Один из возможных алгоритм приведен в #3). В результате длительных манипуляций у вас должно получиться 1010001000111111. А теперь еще раз посмотрите на таблицу #2. (которую вы как бы уже и выучили) и попробуйте то же самое сделать «в уме»: A23F →[math]\underbrace{1010}_{A}\;\underbrace{0010}_{2}\;\underbrace{0111}_{3}\;\underbrace{1111}_{F}[/math] → 1010 0010 0011 1111 Каждой шестнадцатеричной цифре соответствует тетрада (4 штуки) ноликов и единичек. Все, что потом нужно сделать ― «состыковать» эти тетрады. Круто? Вас еще не то ждет! Конкретный рисунок цифр не важен. То, что ноль обозначается кружочком, а единица ― палочкой, всего лишь «историческая условность». Лаосские, кхмерские, тайские цифры Тайланд, 20 бат бирманские цифры Бирма, 50 кьятМожно «нормально считать» в троичной системе представления чисел с цифрами «%», «!», «^» и тогда начало натурального ряда чисел будет выглядеть вот так: %, !, ^, !%, !!, !^, ^%, ^!, ^^, !%%, !%!, !%^, !!%, !!!, !!^, !^%, !^!, !^^, ^%%... Точно также не стоит воспринимать всерьез требования использовать латинские буквы после девятки. ASCII-символы, стоящие после девятки («:;<=>?@») ничем не хуже. Для указания системы представления надо указывать не ее основание, а вектор (упорядоченный массив) ее «цифр». Считая в системах представления с вектором основания, в котором цифры идут «не по порядку» (например, восьмеричная система с вектором «31276504»), можно существенно затруднить понимание происходящего. Существует система представления состоящая из двадцати шести латинских букв, с вектором основания «A B C D E F G H I J K L M N O P Q R S T U V W X Y Z» ― со всеми латинскими буквами в алфавитом порядке. В такой системе представления, к примеру, десятичное число 10 будет записываться как «K». Число «DOG», переведенное из этой системы в десятичную, будет равно 2398. (Проверьте.) Задание #3: Самостоятельно попереводите разные числа из 26-ричной системы в двоичную и десятичную и обратно. Принято указывать основание системы представления внизу-справа от числа. При этом основание записывается в десятичной системе представления. Например ― 1FC16 обозначает число 1FC в шестнадцатеричной системе представления. Задание #4: Заметьте (или проверьте по своей таблице), что [math] 1\overbrace{000\cdots 00_x}^{n} =x^n[/math] где N ― количество нулей после единицы, а x ― основание системы счисления. Алгоритм перевода числа из любой системы в десятичную систему Если перейти от десятичной записи числа к записи числа в p-ричной системе представления, то изменений будет не очень много [math]a_{n}a_{n-1}\cdots a_{1}a_{0}=a_{n}\cdot p^{n}+a_{n-1}\cdot p^{n-1}+\cdots +a_{1}\cdot p+a_{0}=\displaystyle \sum^{n+1}_{k=0} a_{k}\cdot p^k[/math] где ak∈[math]\{0, 1, 2,\cdots , p-1\}[/math] и an ≠ 0 То есть число равно сумме его цифр, умноженных на основание p возведенное в соответствующую степень k. В качестве примеров используем переводы числа «1210» из троичной системы и числа «C00L» из двадцатидвухричной системы в десятичное представление: 12103 = 1⋅33 + 2⋅32 + 1⋅3 + 0 = 27 + 18 + 3 = 4810 C00L22 = 12⋅223 + 0⋅222 + 0⋅22 + 21 = 127776 + 21 = 12779710 Задание #5: Попробуйте попереводить побольше чисел из самых различных систем представления (желательно без использования калькулятора), выполняя все арифметические действия в уме или на листочке. Применив «схему Горнера», получим более эффективную версию алгоритма 12103 = ((1⋅3+2)⋅3+1)⋅3+0 = 16⋅3 = 48 C00L22 = (((12⋅22+0)⋅22+0)⋅22+21 = 127776 + 21 = 127797 Эффективность алгоритма в том, что не требуется возведение в степень ― перевод получается значительно быстрее и короче. Задание #6: Переводите числа из любой системы представления в привычный нам десятичный вид. Вам нужно постоянно быть в боевой готовности ― кто знает, где придется демонстрировать своё искусство . Если под рукой есть только примитивный калькулятор с четырьмя арифметическими действиями (например, калькулятор в телефоне), то по традиционному методу вам придется где-то записывать, а потом набирать промежуточные результаты. По новому методу вам потребуется лишь набирать между цифрами знак умножения, основание системы представления чисел и знак сложения. Если под рукой не оказалось даже примитивного калькулятора, тогда новый алгоритм можно использовать для перевода числа столбиком. Алгоритм перевода числа из десятичной системы в любую систему Мы переводили числа из «странной записи» в «обычную». При вводе десятичных чисел в компьютер требуется обратное действие ― десятичные числа должны быть переведены в «машинный вид». Хотя эта операция обычно выполняется компьютером, для лучшего понимания мы проделаем эту операцию самостоятельно. Обратное действие (перевод из десятичной системы представления в произвольную) обычно выполняется, когда мы хотим ввести информацию в компьютер. Мы живем в «десятичном мире», а компьютер ― в двоичном. Для того, чтобы ввести что-нибудь в «мир компьютера», нам придется переводить числа в «чужую» систему представления. Тут надо уметь делить. Переведем числа обратно в «родную систему представления», последовательно делим число на основание, пока не получится число, меньше чем основание. Результат собирается из последнего частного и остатков в обратном порядке. В первом случае получаем 12103, а во втором ― C00L22. Задание #7: Попробуйте несколько десятичных чисел перевести во всякие «странные системы представления», а затем переведите получившиеся числа обратно в десятичную систему представления. Если вы хорошо делите «в уме» (или используете калькулятор), тогда удобнее использовать другую запись: при каждом делении остаток записывается справа от черты, а частное ― внизу. Ответ считывается в обратном порядке ― снизу вверх. Задание #8: Напишите программу, выполняющую автоматический перевод в любую систему представления чисел. Таблица #1. Цифры разных систем представления чисел Система счисленияОснованиеИспользуемые цифрыдвоичная20,1четверичная40, 1, 2, 3восьмеричная80, 1, 2, 3, 4, 5, 6, 7десятичная100, 1, 2, 3, 4, 5, 6, 7, 8, 9шестнадцатеричная160, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F Таблица #2. Эквиваленты записи чисел в разных системах представления Двоич- наяЧет- ве- рич- наяВось- ме- рич- наяДеся- тич- наяШест- над- цате- рич- наяДвоично-десятичные (BCD ― binary-coded decimal)8.4.2.1Tele- commu- nication 012367.4.2.18.4.-2.-15.4.2.12.4.2.1000000000.000001100.011001100.11000000.00000000.00000000.0000111110000.000101100.110001100.00010000.01110000.00010000.00011022220000.001001100.101001100.00100000.01100000.00100000.00101133330000.001101100.100101100.00110000.01010000.00110000.0011100104440000.010001100.010101100.01000000.01000000.01000000.0100101115550000.010101100.001101100.01010000.10110000.10000000.1011110126660000.011001100.100011100.01100000.10100000.10010000.1100111137770000.011101100.010011100.10000000.10010000.10100000.110110002010880000.100001100.001011100.10010000.10000000.10110000.111010012111990000.100101100.000111100.10100000.11110000.11000000.11111010221210А0001.000011000.011000001.11000111.00000001.00000001.00001011231311B0001.000111000.110000001.00010111.01110001.00010001.00011100301412C0001.001011000.101000001.00100111.01100001.00100001.00101101311513D0001.001111000.100100001.00110111.01010001.00110001.00111110321614Е0001.010011000.010100001.01000111.01000001.01000001.01001111331715F0001.010111000.001100001.01010111.10110001.10000001.1011100001002016100001.011011000.100010001.01100111.10100001.10010001.1100
Теперь от теории перейдем к практике. Вам понадобится переводить из/в шестнадцатеричную, десятичную, восьмеричную, двоичную системы. Итак, разберем все случаи. Будьте внимательны ― здесь есть подводные камни! 16→2 Это просто ― так как 24=16 поэтому каждой шестнадцатеричной цифре ставятся в соответствие четыре бита («ниббл», «тетрада» или «полубайт») по таблице #2. После перевода всего числа, нолики слева («лидирующие нули»), если они есть, разумеется, можно удалить. [math]12FC_{16}\to\underbrace{{\color{red}{000}}1}_{1}\;\underbrace{0010}_{2}\;\underbrace{1111}_{F}\;\underbrace{1100}_{C}\to 1001011111100_2[/math] 2→16 Не на много сложнее ― двоичные разряды объединяются в «четверки» СПРАВА, при этом последняя «четверка» может оказаться «тройкой», «двойкой» или даже одной цифрой, тогда в качестве недостающих разрядов слева дописываются нули. Затем из таблицы #2 вместо «четверок» выписываются соответствующие шестнадцатеричные цифры. [math]1011001011_2\to\underbrace{{\color{red}{00}}10}_{2}\;\underbrace{1100}_{C}\;\underbrace{1011}_{B}\to 2CB_{16}[/math] 8↔2 Переводы из шестнадцатеричной в двоичную систему и из восьмеричной в двоичную очень похожи, только используется разбивка двоичного числа на тройки («триады») битов (23=8). [math]276_8\to \underbrace{{\color{red}{0}}10}_{2}\;\underbrace{111}_{7}\;\underbrace{110}_{6}\to 10111110_2[/math] [math]1011001011_2\to \underbrace{{\color{red}{00}}1}_{1}\;\underbrace{011}_{3}\;\underbrace{001}_{1}\;\underbrace{011}_{3}\to 1313_8[/math] ВНИМАНИЕ! Алгоритмы существуют лишь для переводов 16↔2 и 8↔2. Для перевода 16↔8 надо использовать промежуточную двоичную систему представления чисел. Использование же алгоритмов для перевода в десятичную систему или из нее ― грубейшая ошибка! Вообще, запомните формулу 8↔2↔16. ВНИМАНИЕ! Распространенное заблуждение! У каждого программиста в жизни бывает период, когда он вдруг (по какой-то необъяснимой причине) начинает считать, что числа, в которых не используются буквы, не меняются при переводе в десятичную систему. То есть, например, 12316=12310. Так вот, ЭТО НЕ ТАК! Подумайте, почему. Задание #9: Посчитайте, чему равно 12316 2→10 Часто требуется переводить байты и слова из двоичной системы в десятичную. Лобовое решение, но не самое лучшее [math]101011010101001010001=2^{20}+2^{18}+2^{16}+2^{15}+2^{13}+2^{11}+2^9+2^6+2^4+1=[/math] [math]=1048576+262144+65536+32768+8192+2048+512+64+16+1=1419857[/math] Можно использовать калькулятор «Пуск» [math]\to[/math] «Все программы» [math]\to[/math] «Стандартные» [math]\to[/math] «Калькулятор» и «Вид» [math]\to[/math] «Программист» 2→16→10 Вместо того, чтобы долго и нудно вычислять степени двойки, а потом подсчитывать их сумму, можно быстро перевести число в шестнадцатеричную систему представления чисел, а уже потом из нее ― в десятичную. Экономится куча времени. [math]\underbrace{1010}_{A}\;\underbrace{1001}_{9} = 10\cdot 16+9 = 169[/math] Байт переводится мгновенно и в уме! Запомните несколько степеней 16: 162=256, 163=4096 164=65536 и 165=1048576 ― пригодится!Алгоритм Соденабыстрый алгоритм перевода из двоичной системы в десятичную предложен Уолтером Соденом (Walter Soden) в 1953 году. Сначала переведем число из двоичной системы в восьмеричную. Например,1010110101010010100012→101.011.010.101.001.010.0012→53251218 Далее выполним перевод из восьмеричной системы в десятичную. Разница между системами представления 10 - 8 = 2. Нужно у восьмеричного представления числа удваивать на k-м шаге k ведущих разряда используя десятичную арифметику, и вычесть полученный результат из (k+1) ведущих разрядов при помощи десятичной арифметики. Если заданное число содержит (m+1) восьмеричных цифр, тогда преобразование займет m шагов. Для выделения удваиваемых разрядов используем «разделяющую точку». Это поможет исключить возможные ошибки 1010110101010010100012→53251218→141985710 Если двоичное число не очень длинное. тогда можно перевести непосредственно из двоичного представления в десятичное, не прибегая к промежуточному восьмеричному представлению. Разница между системами представления 10 - 2 = 8. 11111100012→10091010→2 Алгоритм перевода из десятичной системы в двоичную, предложенный Шарлем Розье (Charles P. Rozier) в 1962 году, почти такой же. Сначала переводим число в промежуточную восьмеричную запись. Для этого, заданное в десятичном формате число, удвоим на k-м шаге k ведущих разрядов, используя представление в восьмеричном формате, и сложим полученные(k+1) ведущих разрядов при помощи восьмеричной арифметики. Для заданного числа, содержащего (m+1) цифр преобразование закончится через m шагов. Например, Далее переводим восьмеричное число в двоичное. [math]5325121_8\to 101.011.010.101.001.010.001_2\to 101011010101001010001_2[/math] [math]1419857_{10}\to 5325121_8\to 101011010101001010001_2[/math] получаем двоичный вид числа от последней двоичной цифры к первой. Число последовательно делится на 2 до получения частного равного нулю. Представление числа в двоичной системе – это упорядоченная последовательность остатков от деления в порядке, обратном их получению (старшую цифру числа дает последний остаток, а младшую – первый). Начинаем с n=0 Делим число на 2. Запомним частное qn и остаток an. Если в результате деления частное qn ≠ 0, тогда принимаем qn за новое делимое, увеличиваем n на 1 и возвращаемся к пункту 1. Если в результате деления частное qn = 0, тогда выписываем остатки в обратном порядке anan-1…a1a0. Это и будет число представленное в двоичной системе счисления. Получение двоичного вида числа 147: вычисляем двоичный вид числа начиная с первой двоичной цифры. Для этого придется запомнить степени 2 (210=1024, 29=512, 28=256, 27=128, 26=64, 25=32, 24=16, 23=8, 22=4). Для примера рассчитаем как будет выглядеть двоичное представление того же числа 147, ищем ближайшую степень двойки log2(147)=7,199672... или можно проще 27=128 <147< 28=256 Определившись со степенью n – делим десятичное представление числа на 2n. Результатом будет первая двоичная цифра и остаток. Остаток делится на 2n-1 и так далее, пока остаток не станет меньше 2, а степень нулевой. похоже на третий вариант, но деление заменено на вычитание. Преобразуем десятичное число 84 в двоичное. Определяем, ЧТО должно стоять в каждом разряде двоичного числа: 1 или 0. Находим наибольшую степень 2, меньше или равную заданному числу log2(84)=6,392317... или 26=64 < 84 < 128=27, и ставим 1 в 6-ой разряд. Далее 84–64 = 20, 20 < 32=25, поэтому в 5-ый разряд ставим 0, 20 > 16=24, в 4-ый разряд ставим 1. Остается 20 –16 = 4. 4 < 8=23, ставим 0 в третий разряд. 4=22, – ставим 1 во второй разряд и нули в разряды 1 и 0. Собрав все вместе, получаем 84 = 10101002 10→16 сперва находим последнюю цифру, затем предпоследнюю и так далее... Число в десятичной форме записи 42936 переводится следующим образом: Второй вариант перевода числа из десятичного вида в шестнадцатеричный – сперва находим первую цифру, затем вторую и так далее... Для начала придется запомнить степени 16 (165=1048576, 164=65536, 163=4096, 162=256). Начинаем с вычисления старшего разряда и определения его порядка в шестнадцатеричном виде числе, если десятичное число между 16 миллионами и миллионом – начинаем с деления на 1048576, если между миллионом и 65 тысячами – начнем с деления на 65536 и так далее. log16(24477)=3,64785... 163=4096 Определившись со степенью – делим десятичное представление числа на 16n. Результатом будет первая шестнадцатеричная цифра и остаток. Остаток делится на 16n-1 и так далее, пока остаток не станет меньше 16, а степень нулевой. Для примера рассчитаем шестнадцатеричное представление числа: Преобразуем число 333 в шестнадцатеричное. Найдем наибольшую степень шестнадцати, меньшую или равную заданному числу. log16(333)=2,094844592 или так 162=256<333<4096 =163
Число [math]256[/math] содержится в числе [math]333[/math] только один раз, поэтому во второй разряд записываем единицу. [math]333–256=77[/math]. Число [math]16[/math] содержится в числе [math]77[/math] четыре раза, поэтому в первый разряд записываем четверку. Остается [math]77 – 16\times 4 = 13 = D_{16}[/math], поэтому в нулевой разряд записываем [math]D[/math]. [math]333_{10} = 14D_{16}[/math] 16→10 Обратный процесс – переведем число из шестнадцатеричного вида в десятичный – возьмем число A7B8 и учтем, что каждая шестнадцатеричная цифра всегда в 16 раз больше ближайшей цифры справа: ((A⋅16+7)⋅16+B)⋅16+8=(167⋅16+11)⋅16+8=2683⋅16+8=42936 Задание #10: Переведите из десятичной системы в двоичную систему число 12345678987654321. Задание #11: Переведите из двоичной системы в десятичную систему число 10101010101010101 ОбозначенияНа самом деле, такую глобальную роль шестнадцатеричная и восьмеричная системы представления чисел играют исключительно благодаря тому, что 8=23, а 16=24. Внутри машина считает исключительно «по-двоичному». Шестнадцатеричная и восьмеричная системы представления чисел позволяют вместо длинных цепочек из нолей и единиц записывать более короткие строки из чисел. Эти системы ― промежуточное звено между машиной и человеком, между двоичной и десятичной системами представления чисел. Роль систем представления чисел огромна! К примеру, права доступа к файлу в системе UNIX изменяются из shell'а командой chmod. Эта команда требует задания аргумента (прав доступа) в восьмеричной системе представления чисел! Команда chmod 666 /etc/passwdоткрывает доступ на чтение и запись всем пользователям к файлу паролей в системе UNIX. Даже системные администраторы должны разбираться в восьмеричной системе представления чисел. Так чего же говорить о программистах, пишущих на языке ассемблер! В различных языках программирования числа в этих системах обозначаются по разному. Таблица #3. Сводная таблица для основных языков: C/C++PascalBASICAssemblerDEC12121212 ,12d, 12tHEX0xC$C&hC0Ch, 3F800000rOCT014―&o1414o, 14qBIN――&b11001100b, 1100yНули в начале числа не влияют на его значение. Создатели языка Cи решили по другому. Поскольку никто в здравом уме не начинает числа с нуля (кому охота набивать лишние символы?), ноль в начале числа используется для обозначения признака восьмеричного числа. При всех способах записи регистр букв неважен. Даже строгий на регистры Cи позволяет начинать шестнадцатеричные числа как с 0x, так и с 0X. Кстати, в ранних версиях компиляторов Cи различных фирм шестнадцатеричные числа надо было начинать с 0x0. При программировании на ассемблере если шестнадцатеричное число начинается с буквы, перед ним обязательно должен ставится ноль. Иначе ассемблер подумает, что вы имели в виду переменную Ch, а не число C16. Но некоторые не способны запомнить это правило, ставят ноль впереди всех шестнадцатеричных чисел. В диалект MASM дополнительно к суффиксам D, O, H и B добавлены суффиксы T, Q, R и Y. Дело в том, что с помощью директивы .RADIX ассемблер позволяет менять систему представления чисел, действующую по умолчанию. Раньше, когда систему представления чисел изменяли на шестнадцатеричную, числа вроде 12D, которые с первого взгляда являются обычными шестнадцатеричными, интерпретировались как 1210. Это порождало сеть мелких и незаметных ошибок, мешавших использовать .RADIX 16. Мы будем использовать все способы записи чисел, используя ассемблерный способ как основной. Вы должны хорошо владеть всеми способами.АрифметикаВ любой их систем представления чисел можно по прежнему складывать, вычитать, делить и умножать. Попробуйте самостоятельно решить примеры на сложение и вычитание в шестнадцатеричной, восьмеричной и двоичной системах представления чисел. Задание #11: Попробуйте делить и умножать в этих системах представления чисел. Производите побольше действий в уме, развивайте свои способности быстро считать, запоминать информацию. Программа hiew позволяет вывести на экран дамп файла. Программа debug.exe имеет команду d, выводящую на экран дамп (dump) памяти. Даже FAR manager позволяет при нажатии на клавишу F4 выводить содержимое файла в шестнадцатеричном виде. Теперь вы должны очень хорошо представлять себе и чувствовать шестнадцатеричную и другие системы. Наконец-то вы сможете разобраться в дампе. Существуют специальные шестнадцатеричные калькуляторы, позволяющие считать в этих широкораспространенных системах представления чисел.
Несколько приемов, которые упростят перевод из двоичного в десятеричное представление [math]2^n = 1\overbrace{000\cdots 00}^{n}[/math] ― [math](n+1)[/math]-разрядное двоичное число в котором [math]n[/math] нулей чему равно [math]2^0+2^1+2^2+\cdots +2^{n-2}+2^{n-1}=?[/math] перед нами сумма [math]n[/math] первых членов геометрической прогрессии, где каждый следующий член прогрессии больше предыдущего в 2 раза. Любой член геометрической прогрессии может быть вычислен по формуле: [math]b_{n}=b_{1}\times q^{n-1}[/math] Формула суммы [math]n[/math]-первых членов геометрической прогрессии [math]S_{n}=\frac{b_{1}\times (1-q^{n})}{1-q}[/math] В нашем случае [math]b_1=2^0=1[/math] [math]q=2[/math] [math]S_n=2^n-1[/math] [math]2^0+2^1+2^2+\cdots +2^{n-2}+2^{n-1}=2^n-1[/math] то есть [math]n[/math]-разрядное двоичное число только из единиц соответствует десятичному числу [math]\overbrace{111\cdots 11}^{n}=2^n-1[/math] [math]n[/math]-разрядное двоичное число из чередующихся нулей и единиц, заканчивающееся на единицу, соответствует десятичному числу [math]\overbrace{1.01.01.01\cdots 01.01}^{n}=\frac{1}{3}(2^{n+1}-1)[/math] [math]2^{n+1}[/math] потому что [math]\overbrace{1.01.01.01\cdots 01.01}^{n}=\overbrace{01.01.01.01\cdots 01.01}^{n+1}[/math] [math]n[/math]-разрядное двоичное число из чередующихся нулей и единиц, заканчивающееся на ноль, соответствует десятичному числу [math]\overbrace{10.10.10\cdots 10.10}^{n}=\frac{2}{3}(2^{n}-1)[/math] [math]n[/math]-разрядное двоичное число из чередующихся двух единиц и нуля, заканчивающееся на ноль, соответствует десятичному числу [math]\overbrace{110.110.110\cdots 110}^{n}=\frac{6}{7}(2^{n}-1)[/math] [math]n[/math]-разрядное двоичное число из чередующихся единицы и двух нулей, заканчивающееся на ноль, соответствует десятичному числу [math]\overbrace{100.100.100\cdots 100}^{n}=\frac{4}{7}(2^{n}-1)[/math] Интересно, заметили ли Вы связь между числителем и знаменателем дроби, стоящей перед [math](2^{n}-1)[/math] и повторяющейся частью и периодичностью, с которой эта часть повторяется? вычислим чему равно произведение [math](2+1)(2^{2}+1)(2^{4}+1)\cdots (2^{2^n}+1)=?[/math] Думаете это сложно и нужен калькулятор? Внимание на экран [math]2+1=3=4-1=2^2-1[/math] и [math](a+b)(a-b)=a^2-b^2[/math] тогда [math](2+1)(2^{2}+1)(2^{4}+1)\cdots (2^{2^n}+1)=[/math] [math]=(2^{2}-1)(2^{2}+1)(2^{4}+1)\cdots (2^{2^{n}}+1)=[/math] [math]=(2^{4}-1)(2^{4}+1)\cdots (2^{2^{n}}+1)=\cdots =[/math] [math]=(2^{2^{n}}-1)(2^{2^{n}}+1)=2^{2^{n+1}}-1[/math] А теперь умножим [math]\overbrace{111\cdots 11}^{n}=2^n-1[/math] на [math]2^m[/math] В результате получится [math](2^n-1)\cdot 2^m=2^{n+m}-2^m=\underbrace{\overbrace{11\cdots 11}^{n}\overbrace{000\cdots 00}^{m}}_{n+m}[/math] ― [math](n+m)[/math]-разрядное двоичное число, в котором [math]n[/math] единиц и [math]m[/math] нулей. В разделе «Алгоритм Содена» переводят двоичное число 11111100012 в десятичное 100910, но можно же сделать и вот так [math]1111110001_{2}=2^{10}-2^{4}+1=1024-16+1=1009_{10}[/math] Целая часть от [math]\underbrace{\sqrt{111\cdots 11}}_{2n}=\underbrace{111\cdots 11}_{n}[/math] [math]11^2=3^2=9=1001[/math] [math]111^2=7^2=49=110001[/math] [math]11111^2=31^{2}=961=1111000001[/math] [math](2^{n}-1)^2=2^{2n}-2\cdot 2^{n}+1=2^{2n}-2^{n+1}+1[/math] [math]{\underbrace{11\cdots 11}_n}^2=\underbrace{11\cdots 11}_{n-1}{\overbrace{000\cdots 00}^n} 1[/math] Числа со знакомПрямой (знаковеличинный) кодРано или поздно возникает потребность представлять в двоичном коде отрицательные числа. А как в двоичной системе представить отрицательное число, если мы не можем использовать для это цели минус как в традиционной десятичной системе? Самое простое — отвести один разряд (скажем, старший) под знак числа, используя остальные для представления величины числа. Этот способ называется знаковеличинным или прямым кодом и соответствует обычной записи числа со знаком. Он используется при выводе чисел на индикацию, в АЦП (аналого-цифровых преобразователях), для представления вещественных и двоично-десятичных чисел. В такой кодировке появляются нули двух типов (+0 и -0)Смещенный кодВторой способ представления числа со знаком — представление числа в виде смещенного кода. Для получения смещенного кода какого-либо числа, нужно к этому числу, представленному в прямом коде, прибавить половину наибольшего возможного числа. Благодаря этой операции последовательность всех чисел, начиная со старшего отрицательного и заканчивая старшим положительным, будет представлять из себя простую двоичную прогрессию. Информацию о знаке числа здесь также несет старший разряд, нуль становится единственным. Смещенный код используется в АЦП и ЦАП (цифро-аналоговых преобразователях), в смещенном коде представляется степень вещественного числа.Дополнительный кодПри выполнении операций над целыми числами используется представление чисел в форме «дополнения до двух», или в «дополнительном коде». Положительные числа в такой системе записываются как двоичные без знака, а отрицательные выражаются таким числом, которое, если добавить к положительному числу той же величины, даст в результате ноль. Таблица #4. 4-разрядные двоичные числа, представленные в трех системах деся- тичное представ- ление числадвоичное представление числапрямой кодсмещен- ный коддополни- тельный код+7011111110111+6011011100110+5010111010101+4010011000100+3001110110011+2001010100010+10001100100010000010000000-01000——-1100101111111-2101001101110-3101101011101-4110001001100-5110100111011-6111000101010-7111100011001-8—00001000
В таблице #4 видно, числа в дополнительном коде отличаются от чисел в смещенном коде инверсным значением старшего значащего разряда (СЗР). Точно также, как и при других формах представления, СЗР несет информацию о знаке. Здесь имеется только один ноль, представляемый нулевыми состояниями всех разрядов.Более подробно о дополнительном кодеПо определению, для каждого натурального числа [math]n[/math] существует одно и только одно отрицательное число, обозначаемое [math]-n[/math], которое дополняет [math]n[/math] до нуля: [math]n + (-n) = 0.[/math]Найдем такое 4-битное число, которое при сложении с числом 5 (0101) дало бы нам ноль, переносом в 5-й бит мы пренебрегаем. Теперь самостоятельно заполните таблицу #5 Таблица #5 hex- числодвоичное числоn-nТак как [math]n = -(-n)[/math], поэтому, таблицу #5 можно сократить в два раза Таблица #6 число nчисло -nHexbinHexbin000000000010001F111120010E111030011D110140100C110050101B101160110A101070111910018100081000При этом оказалось, что числа 0 и 8 являются сами для себя отрицательными числами. Если не рассматривать числа 0 и 8, числа в левой половине таблицы объединяет то, что самый старший (четвертый) бит у них равен 0, а у чисел в правой половине самый старший бит равен 1. Пусть самый старший бит считается знаковым. Если он равен 0, число будем считать положительным, если 1 — отрицательным. Мы только что изобрели способ представления положительных и отрицательных чисел в двоичной системе, так называемый «код дополнения до двух», или «дополнительный код». При этом, число 0 может быть только положительным, а четырехбитовое число 8 — только отрицательным. Таблица #7 Положительное 4-битное число nОтрицательное 4-битное число -nдесятич- ноеhexbinдесятич- ноеhexbin+000000-0——+110001-1F1111+220010-2E1110+330011-3D1101+440100-4C1100+550101-5B1011+660110-6A1010+770111-791001+8——-881000Компьютер различает целые числа без знака (неотрицательные) и целые числа со знаком. Положительные и отрицательные числа расположены как бы в «кольце». Если вычитать в цикле из байтовой переменной на каждом шаге единицу, то, когда значение переменной достигнет нуля, следующим вычитанием мы получим в переменной значение FF16=25510 — максимальное беззнаковое число, которое может быть записано в байт, и одновременно значение -1, если рассматривать его, как число со знаком. При продолжающемся вычитании мы проходим весь диапазон отрицательных чисел и достигаем минимального значения, которое может быть записано в байт — 8016=-12810. На следующем шаге мы получаем значение 7F16=+12710 — максимальное знаковое число, которое может быть записано в байт. Если продолжить вычитание — мы опять достигаем значение нуля. Таблица #8. Дополнительный код дополни- тельный двоичный кодHexчисло со знакомчисло без знака0000 00000000000 00011110000 0010222. . . .0111 11117F1271271000 000080-1281281000 000181-127129. . . .1111 1110FE-22541111 1111FF-1255Для целых чисел со знаком положительное число записывается в прямом коде (как есть). Отрицательное — в дополнительном коде. Преобразовать положительное число в отрицательное можно пятью способами. Классический способ: взять число в прямом коде, преобразовать его в инверсный код. Для этого все нули этого числа необходимо заменить на единицы, а единицы на нули. К числу в инверсном коде добавить 1. На рисунке пример преобразования числа +7 в –7. Второй способ: взять число, отнять от него единицу, преобразовать в двоичный вид, заменить нули на единицы, а единицы на нули. 7-1 = 6 = 00000110 → 11111001 = -7 Быстрый способ: двигаемся справа налево и пропускаем нули до первой единицы, после нее заменяем единицы на нули, а нули на единицы. 20 = 00010100 → 11101100 = -20 Практический способ: чтобы получить N-разрядное отрицательное число возьмем число 2N(для байта 28 = 256, для слова 216=65536, для двойного слова 232=4294967296, для учетверенного cлова 264=18446744073709551616) и отнимем от него наше число, получившееся число преобразуем в двоичный или hex-код: 256 – 7 = 249 = 11111001 = F9 = – 7. Студенческий способ: щелкаем по клавише «Пуск», открываем «Стандартные приложения», запускаем «Калькулятор» в режиме «Программист», теперь щелкаем по клавишам и на экране калькулятора появится «–7». А теперь жмем на клавишу F8 или мышкой по переключателю с надписью . Что мы видим на экране калькулятора? Число «1111111111111111111111111111111111111111111111111111111111111001». Многовато, на экране — учетверенное слово. Жмем на F4 или мышью по переключателю с надписью . Наше число приобрело «нормальный вид» «11111001». На самом деле, нет необходимости пересчитывать отрицательные числа. Вы пишете –7, а транслятор автоматически подставит туда, где нужно число 11111001. Если мы пренебрегаем знаковым разрядом, то диапазон чисел увеличивается в 2 раза: 27 = 128 28 = 256
Представление чисел с плавающей запятойУ десятичных чисел каждая позиция числа соответствует степени числа 10, то есть число [math]1234,56=1\cdot 10^3+2\cdot 10^2+3\cdot 10^1+4\cdot 10^0+5\cdot 10^{-1}+6\cdot 10^{-2}[/math]. Запятая показывает границу между позицией, соответствующей [math]10^0[/math], и дробной частью. В дробной части позиции числа также являются степенями числа 10, но эти степени теперь отрицательные. Дробные двоичные числа записываются аналогично дробным десятичным, но основание системы счисления здесь 2, а не 10. Например,[math]1,101_2=1+\frac{1}{2}+\frac{1}{8}=1\frac{5}{8}=1,625[/math] Чтобы получить эквивалент целого числа в двоичной системе, мы использовали последовательное деление на 2. Чтобы получить эквивалент десятичной дроби, используем обратную операцию — последовательное умножение на 2. Для перевода десятичной дроби в двоичную систему можно использовать четыре способа, а уж который из них Вам покажется более удобным — выбирайте сами. Способ первый. Дробную часть числа последовательно умножают на 2 пока либо дробная часть не станет равной нулю, либо не будет получено необходимое количество разрядов. Целые значения, получаемые при умножении, будут являться эквивалентом десятичной дроби в двоичной системе. Посмотрите конкретный пример Способ второй. Умножать на два 8 раз чтобы получить 8-двоичных разрядов дробной части долго и утомительно. А давайте 0,14159 умножим сразу на [math]2^8=256[/math], что получится? [math]0,14159\times 256=36,2470410=100100_2[/math]. 100100 это шесть разрядов, а если добавить 2 нуля слева? 00100100 ничего не напоминает? Добавим "0," и "0,00100100" двоичный эквивалент числа 0,14159. Нужно получить 16 разрядов после запятой? Смело умножайте на [math]2^{16}=65536[/math], переводите в двоичный вид и не забудьте добавить недостающие нули слева. Способ третий. Любое дробное двоичное число можно представить как [math]x_1\cdot 0,5 + x_2\cdot 0,25 + x_3\cdot 0,125 +\cdots[/math]где [math]x_1, x_2,\cdots x_n[/math] — нули или единицы, то есть [math]0,1_{10}=0,0001100_2[/math]. Способ четвертый. Переведем в двоичную систему, например, число [math]0,27=\frac{27}{100}[/math]. Ближайшая к 100 степень двойки [math]2^7=128[/math]. [math]\frac{27}{100}\times\frac{1,28}{1,28}=\frac{34,56}{128}=\frac{32+2+1}{128}=\frac{1}{4}+\frac{1}{64}+\frac{1}{128}=2^{-2}+2^{-6}+2^{-7}=[/math] [math]=0,01+0,000001+0,0000001=0,0100011_2[/math]Перевод десятичного дробного числа 0,406 в шестнадцатеричную систему делается аналогично переводу к двоичному виду: [math]0,616\times 16=9,856[/math] и так далее, то есть [math]0,406_{10}=0,67EF9..._{16}[/math] . Для перевода вещественного целого числа в двоичную или шестнадцатеричную систему необходимо целую и дробную часть перевести по отдельности в двоичную или шестнадцатеричную систему, а затем соединить их. Например, число [math]\pi[/math] равное [math]3,14159[/math] должно выглядеть вот так [math]11,00100100_2[/math], или так [math]3,243F_{16}[/math]. Существует два способа записи вещественных чисел: Первый способ — целая часть числа отделяется от дробной символом запятой, расположенной между конкретными разрядами в фиксированной позиции. Такой способ записи называют представлением вещественных чисел с фиксированной запятой. Возьмем 16 разрядное число и отведем под целую часть числа 8 разрядов, а под дробную 8 разрядов, тогда максимально большое число, которое можно записать таким способом:[math]11111111,11111111_2=255,99609375 \approx 256[/math], а минимальное возможное число — [math]00000000,00000001_2=\frac{1}{256} \approx 0,00390625[/math]. Запомните эти числа. Второй способ применяется в астрономии, физике, химии, математике для записи очень больших либо очень маленьких чисел. Например, скорость света в вакууме составляет около [math]300000000[/math] м/с, а гравитационная постоянная примерно равна [math]0,0000000000667[/math] Нм2/кг2. Для удобства работы с такими числами используют так называемую экспоненциальную форму записи. Вместо [math]300000000[/math] пишут [math]3\cdot 10^8[/math], а вместо [math]0,0000000000667[/math] — [math]6,67\cdot 10^{-11}[/math]. Любое число представляется в виде [math]\pm C,M\cdot R^P[/math], где [math]C[/math] — целая часть числа, дробную часть числа представляющую значащие разряды — [math]M[/math], называют мантиссой. [math]R[/math] — основание системы счисления. [math]P[/math] — показатель степени, определяющий на сколько разрядов вправо или влево необходимо переместить запятую в мантиссе, называется порядком числа. Число [math]C[/math] должно быть [math]1\le C < R[/math]. Такой способ записи называют представлением вещественных чисел с плавающей запятой. Как и в первом случае возьмем 16-разрядное число. Под мантиссу отведем 8 разрядов и 8 разрядов под порядок числа (из 8 разрядов 1 разряд отводится под знак и 7 разрядов под число), в этом случае максимальновозможное число [math]2^{127}\approx 10^{38}[/math], а минимальновозможное — [math]2^{–127}\approx 10^{–38}[/math]. Сравните их с числами полученными в первом случае Для представления в компьютере дробного числа первый разряд числа считается знаковым и обозначает знак мантиссы. За ним следует мантисса (число со значением больше или равным 1 и меньшим 2) и порядок (степень числа 2). Представление чисел в виде мантиссы и порядка позволяют сводить умножение и деление чисел к сложению и вычитанию показателей степеней, а возведение в степень и извлечение корня — к умножению и делению на показатель степени, что упрощает и сокращает сложные вычисления. Вещественные числа в памяти компьютера хранятся в нормализованном виде, то есть оно обязательно должно иметь следующий вид: [math](-1)^S\times 1,M\times 2^P[/math],где значение [math]S[/math] определяет знак ([math]S=0[/math] — число положительное, [math]S=1[/math] — отрицательное), [math]M[/math] — двоичная мантисса числа, [math]P[/math] — порядок двоичного числа. Из-за того, что целая часть вещественного числа в двоичной системе всегда равна 1, его, для увеличения разрядности дробной части, предпочитают «хранить в уме». Для упрощения вычислений значение порядка хранят не в дополнительном коде (то есть не в виде целого со знаком), а в смещенном коде в виде суммы с некоторым числом, это позволяет облегчить сравнение вещественных чисел — в большинстве случаев достаточно сравнить их экспоненты. Обратите внимание, что в смещенном порядке у отрицательных чисел и нуля в двоичном представлении старший разряд нулевой. В смещенном коде число суммируется с константой, которая для N-битного кода равна [math]2^{N-1}-1[/math]. Десятичное значениеСмещенный код8-битный11-битный15-битный-16383——0000h-1023—000h3C00h-12700h380h3F80h. . .-17Eh3FEh3FFEh07Fh3FFh3FFFh180h400h4000h. . .1280FFh47Fh407Fh1024—7FFh43FFh16384——7FFFhМикропроцессоры семейства x86 х64 могут работать с 32/64/80-битными вещественными числами (одинарная/двойная/повышенная точность), а также со 128-битными, состоящими из четырех упакованных вещественных чисел одинарной точности. Коли- чество битТипПоле знакаПоле порядкаКонстантаПоле мантиссыДиапазон принимаемых значений32REAL41 бит8 бит27-1=12723 битаОт [math]\pm 1,18\cdot 10^{-38}[/math] до [math]\pm 3,4\cdot 10^{38}[/math]64REAL81 бит11 бит210-1=102352 битаОт [math]\pm 2,23\cdot 10^{-308}[/math] до [math]\pm 1,79\cdot 10^{308}[/math]80REAL101 бит15 бит214-1=1638364 битаОт [math]\pm 3,37\cdot 10^{-4932}[/math] до [math]\pm 1,18\cdot 10^{4932}[/math]Число 193,75 в формате вещественного числа, занимающего два слова: [math]193,75\to 4341C000_{16}[/math][math]193,75>0 \to[/math] знак=0 [math]log_{2}(193,75)=7,598053[/math] степень [math]7+127=134=10000110_2[/math] разрядов в мантиссе - степень [math]23-7=16[/math] мантисса [math](193,75-2^{7})\cdot 2^{16}=66,75\cdot 2^{16}=66,75\cdot 65536=4374528=10000101100000000000000_2[/math] Преобразуем вид число в бинарное представление: 01000011010000011100000000000000Знак числа 0=«+» 1=«—»01000011010000011100000000000000Порядок 86h=+7 в смещенном коде [math]10000110_2=134_{10}=7+127[/math]01000011010000011100000000000000мантисса числа=[math](1),1000001110000_2[/math] Рис. Форматы представления вещественных чисел
Запись одного и того же числа в разных представлениях, естественно, будет отличаться. Число 193,75 может быть представлено как: Коли- чество битТипшестнадцатеричное представление числа 193,7532REAL44341C000h64REAL84068380000000000h80REAL104006C1C0000000000000hПреобразуем число 4068380000000000h в бинарное представление: 0100.0000.0110.1000.0011.1000.0000.0000.0000.0000.0000.0...0.0000.0000.0000.0000Знак числа 0=«+»010000000110100000111000000000000000000000000...00000000000000000Порядок =+7 [math]10000000110_2=1030_{10}=7+1023[/math]010000000110100000111000000000000000000000000...00000000000000000мантисса числа=(1),1000001110000000000000000000000000...000000000000bПреобразуем число 4006C1C0000000000000h в бинарное представление: 0100000000000110110000011100000000000000000000000000000000...0000Знак числа 0=«+»0100000000000110110000011100000000000000000000000000000000...0000Порядок =+7 [math]100000001100000_2=16480_{10}=7+16383[/math]0100000000000110110000011100000000000000000000000000000000...0000мантисса числа=1,10000011100000000000000000000000000000000...0000bТипы REAL4, REAL8 и REAL10 соответствуют стандартным вещественным типам в Pascal/Delphi и C/C++ AsmPascal/DelphiC/C++ФормулаREAL4Singlefloat[math](-1)^{S}\times(1+m\times 2^{-23})\times 2^{P-127}[/math]REAL8Doubledouble[math](-1)^{S}\times(1+m\times 2^{-52})\times 2^{P-1023}[/math]REAL10Extendedlong double[math](-1)^{S}\times m\times 2^{P-16446}[/math]Первые два типа (REAL4 и REAL8) являются «упакованными», а REAL10 — «неупакованным» типом, в нем старший, всегда единичный, бит нормализованной мантиссы хранится в явном виде. Все вычисления внутри FPU происходят после автоматического преобразования типов REAL4 и REAL8 в REAL10. Для записи вещественного числа в программе пересчет вещественного десятичного числа из десятичной системы в hex или двоичную самому делать не обязательно, просто запишите число, используя в качестве разделителя точку вместо запятой: Код (ASM): pi dt 3.1415926535897932384626433832795 А компилятор сам подставит 32-битный эквивалент в случае с директивой dd 64-битный для директивы dq 80-битный для директивы dt Перевожу 193,75 в REAL4 (мантисса 23 бита, степень смещается на 127) вычисляем степень [math]log_2(193{,}75)=7{,}598053[/math] округляем в меньшую сторону 7. [math]p=127+7=134=86_{16}[/math] (193,75-27)x 223-7=(193,75-128)x 65536=4308992=41C00016 [math]\underbrace{0.100}_{4}\underbrace{0 011}_{3}\underbrace{0.100}_{4}\underbrace{0001}_{1}\underbrace{1100}_{C}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}[/math] в REAL8 (мантисса 52 бита, степень смещается на 1023) [math]p=1023+7=1030=406_{16}[/math] (193,75-27)x 252-7=2313372464840704=838000000000016 [math]\underbrace{0.100}_{4}\underbrace{0 000}_{0}\underbrace{0110.}_{6}\underbrace{1000}_{8}\underbrace{0011}_{3}\underbrace{1000}_{8}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}[/math] в REAL10 (мантисса 63 бита, степень смещается на 16383) [math]p=16383+7=16390=4006_{16}[/math] 193,75 x 263-7=13961158844848537600=C1C000000000000016 [math]\underbrace{0.100}_{4}\underbrace{0 000}_{0}\underbrace{0000}_{0}\underbrace{0110.}_{6}\underbrace{1{,}100}_{С}\underbrace{0001}_{1}\underbrace{1100}_{С}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}\underbrace{0000}_{0}[/math] Задание #12: Теперь читателю предлагается потренироваться в переводе чисел. Пред вами два простых примера: 0.125 и 0.625. Можно усложнить их: 23.125 и 456.625. Проверьте свои ответы, записав результат в переменную типа dword и посмотрите под отладчиком число в стеке FPU.
Специальные численные значения ЗнакПорядок (79-64 биты)Мантисса (63-0 биты)Название числа0000...00000000000...00000000000+01111...11111111111...11111111111-00111...11111111000...00000000000[math]+\infty[/math] (+бесконечность)1111...11111111000...00000000000[math]-\infty[/math] (-бесконечность)[math]x[/math]000...00000000[math]xxx...xxxxxxxx[/math]Денормализованные числа, используются для работы с очень маленькими числами от [math]\pm 10^{-4932}[/math] до [math]\pm 10^{-16445}[/math][math]x[/math]111...1111111110[math]x...xxxxxxxx[/math]Нечисло типа SNAN (Signal Non A Number). Среди [math]x[/math] есть единицы. FPU реагирует на появление SNAN возбуждением исключения недействительной операцииНесмотря на большой диапазон вещественных значений, представимых в регистрах данных FPU, значения, например, [math]+\infty[/math] и [math]-\infty[/math] находятся за пределами этого диапазона. Для того, чтобы иметь возможность реагировать на некоторые вычислительные ситуации, которые могут возникнуть в результате математических операций в блоке FPU, предусмотрены специальные комбинации битов, называемые специальными численными значениями. Над специальными численными значениями можно выполнять некоторые операции. Программист может и сам кодировать специальные численные значения директивой DT (х – любое значение бита). Если в результате вычислений мантисса и порядок равны нулю, то, несмотря на скрытую единицу в целой части числа REAL4/8, все число полагается равным нулю. Любой конечный результат операции FPU, который из-за ограниченности поля порядка не может быть представлен в 23/52/64 разрядах для REAL4/8/10, соответственно вызывает особый случай некорректности представления числа.
Как быстро перевести вещественное число в десятичном виде в REAL4, REAL8, REAL10 Коли-чество битТипПоле знакаПоле порядкаКонстантаЦелая частьПоле мантиссыДиапазон принимаемых значений32REAL41 бит8 бит[math]2^{8-1}-1=127[/math]скрыта23 битаОт ± 1,18x10-38 до 3,4x103864REAL81 бит11 бит[math]2^{11-1}-1=1023[/math]скрыта52 битаОт ± 2,23x10-308 до 1,79x1030880REAL101 бит15 бит[math]2^{15-1}-1= 16383[/math]явно указана63 битаОт ± 3,37x10-4932 до 1,18x104932Представить число -240,9375 в формате REAL4 -240,9375 < 0 → Знак=1 log2(240,9375)=7,912515145→ степень =7 степень в смещенном виде 7+127=134=1000.0110b 23 разряда (мантисса) - 7 (степень) = 16 мантисса 216×(240,9375-27)=7401472=70F000h=111.0000.1111.0000.0000.0000b знакстепеньмантиссаbin11000011011100001111000000000000hexC370F000Представить число 15,92 в формате REAL8 15,92>0 → Знак=0 log2(15,92)=3,992768431 → степень = 3 степень в смещенном виде 3+1023=1026=100.0000.0010b 52 разряда (мантисса) - 3 (степень) = 49 мантисса 249×(15,92-23)=249×7,92=4458563631096791,04~4458563631096791=FD70A3D70A3D7h= =1111.1101.0111.0000.1010.0011.1101.0111.0000.1010.0011.1101.0111b 15,92=402FD70A3D70A3D7 знакстепеньмантиссаbin0100000000101111110101110000101000111101011100001010001111010111hex402FD70A3D70A3D7Представить число 6,1429 в формате REAL10 6,1429>0 → Знак=0 log2(6,1429)=2,61892 → степень = 2 степень в смещенном виде 2+16383=16385=100.0000.0000.0001b 63 разряда (мантисса) - 2 (степень) = 61 мантисса 261×6,1429=14164563021298800577,7408~14164563021298800578=C492A305532617C2h= =1100.0100.1001.0010.1010.0011.0000.0101.0101.0011.0010.0110.0001.0111.1100.0010b 6,1429=4001C492A305532617C2 знакстепеньцелая частьмантиссаbin01000000000000011100010010010010101000110000010101010011001001100001011111000010hex4001C492A305532617C2Какому числу типа float соответствует 8027B2A6h=1000.0000.0010.0111.1011.0010.1010.0110b ? знакстепеньмантисса1000.0000.0010.0111.1011.0010.1010.0110-02601638степень нулевая, значит число денормализованное, то есть целая часть равна нулю Какому числу типа double соответствует запись C06E4D1000000000? знакстепеньмантиссаbin1100000001101110010011010001000000000000000000000000000000000000hexC06E4D1000000000 вычисляем знак знак=1 число <0 порядок 100000001102=103010 вычисляем степень 1030-1023=7 обрабатываем мантиссу 52(разрядов в мантиссе)-7(степень)=45 11100100110100010000000000000000000000000000000000002=402538078876467210 4025380788764672/245=4025380788764672/35184372088832=114,408203125 114,408203125+27=114,408203125+128=242,408203125 учтем, что число отрицательное результат: -242,408203125 Из шестнадцатеричного представления получить десятичное число дана шестнадцатеричная свертка 2F16F10016=0010.1111.0001.0110.1111.0001.0000.00002 знак.порядок.мантисса=0.01011110.00101101111000100000000 знак=0 --> число положительное порядок 010111102=9410 степень 94-127=-33 мантисса 1.001011011110001000000002=989209610 число (9892096*2-23)*2-33= 9892096*2-56=9892096/72057594037927936= =+1,3728040926253015641123056411743*10-10 или вот так мантисса 001011011110001000000002=150348810 число (1503488*2-23+1)*2-33=(1503488+223)*2-23-33=(1503488+8388608)/72057594037927936=+1,3728040926253015641123056411743*10-10 Формула для расчета денормализованных чисел REAL4(-1)знак ×2-149× мантиссаREAL8(-1)знак ×2-1074× мантиссаREAL10(-1)знак ×2-16445× мантисса[math]F=(-1)^{sign}\cdot 2^{-149} \cdot mantiss =-1 \cdot 2^{-149} \cdot 01001111011001010100110=[/math] [math]=-2601638\cdot 1,4012984643248170709237295832899\cdot 10^{-45} =[/math] [math]=-3,6456713341290884347638699856112\cdot 10^{-39}[/math] FPU ХАКИ FPU Hacks, когда вычисления делают приближенно через integer типы. (Естественно это либо через память, либо если FP регистры позволяют делать integer операции над ними. Быстрая конвертация в Integer Код (C): int FastFloatToInt(float f) { f += (float)(3 << 21); return (*((int*) &f) - 0x4ac00000) >> 1; } Быстрый квадратный корень Ошибка при вычислении не превышает 5% Код (C): float FastSqrt(float f) { int t = *(int*) &f; t -= 0x3f800000; t >>= 1; t += 0x3f800000; return *(float*)&t; } Approximations that depend on the floating point representation Вещественное число представляется в формате [math]m\times 2^{p}[/math] Квадратный корень равен [math]\sqrt {m}\times 2^{(\frac{p}{2})}[/math] аналогичные формулы применимы для кубических корней и логарифмов. На первый взгляд, это не упрощает вычисление квадратного корня, но предположим, что требуется только приближение: тогда просто [math]2^{(\frac{p}{2})} [/math] хорошо на порядок. Некоторые степени [math]p[/math] будут нечетными, поэтому при вычисления квадратного корня например от числа 3141.59 = 3.14159×103 вместо того, чтобы иметь дело с дробными степенями основания, умножаем мантиссу на основание и вычитаем единицу от степени, чтобы сделать степень четной. Скорректированное представление станет эквивалентом [math]\sqrt{31.4159×10^{2}} = \sqrt{31.4159}\times 10^{1}[/math]. Если берется целая часть скорректированной мантиссы, могут быть только значения от 1 до 99, и их можно использовать в качестве индекса в таблице из 99 предварительно вычисленных квадратных корней для завершения оценки. [math]a[/math]ближайший корень[math]\sqrt{a}[/math]от 1 до 2.51(=12)1от 2.5 до 6.54(=22)2от 6.5 до 12.59(=32)3от 12.5 до 20.516(=42)4от 20.5 до 30.525(=52)5от 30.5 до 42.5 36(=62)6от 42.5 до 56.549(=72)7от 56.5 до 72.564(=82)8от 72.5 до 90.581(=92)9от 90.5 до 100100(=102)10Для компьютера с основанием шестнадцать потребуется таблица большего размера, но для компьютера с основанием два потребуются только три записи: возможные биты целой части скорректированной мантиссы равны 01 (степень даже при отсутствии сдвига, помня, что нормализованная число с плавающей запятой всегда имеет ненулевую старшую цифру) или, если степень была нечетной, 10 или 11, это первые два бита исходной мантиссы. Таким образом, 6,25 = 110,01 в двоичном формате, нормализованное до 1,1001 × 22 четной степени, так что парные биты мантиссы равны 01, в то время как 0,625 = 0,101 в двоичной системе нормализуется до 1,01 × 2-1 нечетной степени, поэтому корректировка составляет 10,1 × 2-2, а парные биты равны 10. Обратите внимание, что бит младшего разряда мощности отражается эхом в бите старшего разряда попарной мантиссы. У четной мощности бит младшего разряда равен нулю, а скорректированная мантисса начинается с 0, тогда как для нечетной мощности этот бит равен единице, а скорректированная мантисса начинается с 1. Таким образом, когда число уменьшается вдвое, бит младшего разряда сдвигается, чтобы стать первым битом попарной мантиссы. Таблица только с тремя записями может быть увеличена за счет включения дополнительных бит мантиссы. Однако с компьютерами, чем вычислять интерполяцию в таблицу, часто лучше найти более простые вычисления, дающие эквивалентные результаты. Теперь все зависит от точных деталей формата представления, а также от того, какие операции доступны для доступа и управления частями числа. Например, Fortran предлагает функцию EXPONENT (x) для получения степени. Усилия, затраченные на разработку хорошего начального приближения, должны окупаться за счет исключения дополнительных итераций процесса уточнения, которые потребовались бы для плохого приближения. Поскольку их немного (одна итерация требует деления, сложения и деления пополам), ограничение является серьезным. Для компьютеров, использующих IEEE представление вещественного числа, методом Ньютона можно получить очень быстрое приближение к квадратному корню. Следующий алгоритм основан на том, что формат с вещественного числа по основанию два аппроксимируется логарифм по основанию 2. Это [math]\log _{2}(m\times 2^{p})=p+\log _{2}(m)[/math] Таким образом, для 32-разрядного числа с плавающей запятой одинарной точности в формате IEEE (где степень суммируется с числом 127), вы можете получить приблизительный логарифм, интерпретируя его двоичное представление как 32-битное целое число, масштабируя это от [math]2^{-23}[/math] и вычитание смещения 127, т. е. [math]x_{\text{int}}\cdot 2^{-23}-127\approx \log _{2}(x)[/math]. Например, 1.0 в шестнадцатеричном виде 0x3F800000 будет представлять [math]1065353216=127\cdot 2^{23}[/math] если рассматривать как целое число. Используя формулу выше, вы получите [math]1065353216\cdot 2^{-23}-127=0[/math], как и ожидалось от [math]\log _{2}(1.0)[/math]. Аналогичным образом вы получаете 0,5 из 1,5 (0x3FC00000). Чтобы получить квадратный корень, разделите логарифм на 2 и преобразуйте значение обратно. Следующая программа демонстрирует эту идею. Обратите внимание, что младшему разряду экспоненты намеренно разрешено распространяться в мантиссу. Один из способов обосновать шаги в этой программе - предположить, что [math]b[/math] - это смещение экспоненты, а [math]n[/math] - количество явно сохраненных бит в мантиссе, а затем показать, что [math](((x_{\text{int}}/2^{n}-b)/2)+b)\cdot 2^{n}=(x_{\text{int}}-2^{n})/2+((b+1)/2)\cdot 2^{n}[/math]. Код (C): /* Assumes that float is in the IEEE 754 single precision floating point format */ #include <stdint.h> float sqrt_approx(float z) { union { float f; uint32_t i; } val = {z}; /* Convert type, preserving bit pattern */ /* * To justify the following code, prove that * * ((((val.i / 2^m) - b) / 2) + b) * 2^m = ((val.i - 2^m) / 2) + ((b + 1) / 2) * 2^m) * * where * * b = exponent bias * m = number of mantissa bits */ val.i -= 1 << 23; /* Subtract 2^m. */ val.i >>= 1; /* Divide by 2. */ val.i += 1 << 29; /* Add ((b + 1) / 2) * 2^m. */ return val.f; /* Interpret again as float */ } Быстрый обратный квадратный корень Если x > 0.25, то ошибка меньше 0.6% Код (C): float FastInvSqrt(float x) { int tmp = ((0x3f800000 << 1) + 0x3f800000 - *(long*)&x) >> 1; float y = *(float *)&tmp; return y * (1.47f - 0.47f * x * y * y); } Алгоритм принимает 32-битное число с плавающей запятой (одинарной точности в формате IEEE 754) в качестве исходных данных и производит над ним следующие операции: Трактуя 32-битное дробное число как целое, провести операцию y0 = 0x5F3759DF − (x >> 1), где >> — битовый сдвиг вправо. Результат снова трактовать как 32-битное дробное число. Для уточнения можно провести одну итерацию метода Ньютона: y1 = y0(1,5 − 0,5x(y0)2). Корректная по меркам современного Си реализация, с учётом возможных оптимизаций и кроссплатформенности: Код (C): float Q_rsqrt( float number ) { const float x2 = number * 0.5F; const float threehalfs = 1.5F; union { float f; uint32_t i; } conv = {number}; // member 'f' set to value of 'number'. conv.i = 0x5f3759df - ( conv.i >> 1 ); conv.f *= threehalfs - x2 * conv.f * conv.f; return conv.f; }
Программа для перевода REAL10 в строку Код (ASM): $MagicLog2_32 equ 1292913987 ; = log(2) * 2^32 dp_1 equ 17 ; = decimal places minus 1 dp4 equ dp_1+4 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ ; Input: ; esi = 10 ; EXPONENTREAL_10KM MACRO cdq xor eax,edx sub eax,edx ; make number positive and dl,2 add byte ptr[edi+22],dl;if eax>=0 then "plus" else "minus" mov edx,4294968 ;2^32/1000 mul edx ;extend first digit shrd ecx,edx,8 ;digit 1 in ECX mul esi ;extend second digit shrd ecx,edx,8 ;digit 2 in ECX mul esi ;extend third digit shrd ecx,edx,8 ;digit 3 in ECX mul esi ;extend fourth digit shrd ecx,edx,8 ;digit 4 in ECX or [edi+23],ecx ;make ASCII string ENDM ;--------------------------------------------- $maxexp equ 90 $maxcounter equ 2*$maxexp+1 ;--------------------------------------------- .data realexp4931 dt 1.0e+4931 shifttableX dd shift0X ; +0 dd shift1X ; +4 dd shift2X ; +8 dd shift3X ;+12 dd shift4X ;+16 ;--------------------------------------------- cntr = -$maxexp REPEAT $maxcounter IF cntr NE 0 dt @CatStr(<1.0e>,%-cntr) ELSE PowerTable12 dt 1.0 ENDIF dw 0 cntr = cntr + 1 ENDM ;---------------------------------------------------- .code align 16 Eprst proc ; locals, buffer and args pReal10 equ dword ptr [esp+16+buffer] pStr equ pReal10+4 iExp equ dword ptr [esp-4];0+4=4 aExp equ iExp-4 ;4+4=8 realx equ aExp-4*3 ;8+12=20 temp equ realx-4*3 ;20+12=32 buffer = 20h ; push ebx push esi push edi sub esp, buffer mov ecx, pReal10 ; --------------------------- ; remove sign ; move real10 to realx ; if iExp=0 we use this realx ; --------------------------- mov edx, dword ptr [ecx+6] add edx, edx; if CF=1 then negative mov edi, pStr sbb eax, eax; if CF=0 then EAX = 0 else EAX = -1 and eax, 0Dh mov aExp, 0 or eax, 2C0020h mov [edi], eax; if CF=0 then EAX = 2C0020h else EAX = 2C002Dh shr edx, 17 ; remove sign and if EDX = 0 go to _expzero mov ebx, [ecx+0] mov ecx, [ecx+4] mov [realx+0], ebx mov [realx+4], ecx mov [realx+8], edx ; only exponent jz _expzero ; if exponent = 0 _expnotzero: cmp edx, 7FFFh je _isinfinity ; test ecx, ecx jns _erro1 ; if integer = 0 and exponent <> 0 then ERROR mov ecx, pReal10 fld tbyte ptr [ecx] jmp _begin ; ------------------------ ; exponent = 0 ; ------------------------ _expzero: or ebx, ecx jz _iszero ; if edx = ebx = ecx = 0 is zero mov ecx, pReal10 fld tbyte ptr [ecx] ;------------------------- ; It is Denormal or ; Pseudo-Denormal ;------------------------- fld realexp4931 ; st(0)=1.0e+4931 mov aExp,-4931 fmul ; normalization fld st ; get a copy fstp tbyte ptr realx ; mov ebx, [realx+0] and [realx+8],7FFFh ; remove sign mov eax, [realx+4] mov edx, [realx+8] ;--------------------- ; start ;--------------------- _begin: mov esi, 10 ;---------------------------------- ; iExp= 4 * Exp because we multiply ; by 2^16 and divide by 2^14 only ;---------------------------------- mov eax, $MagicLog2_32 ; log(2)*2^32 sub edx, 3FFFh ; =16383 imul edx mov iExp, edx or edx,edx jz short _getrealx ;edx = 0 ? cmp edx, -1 je _new ;integer multipication to 10 cmp edx, $maxexp jg @F cmp edx,-$maxexp jge short _load ;------------------------------------------------ @@: neg edx fldl2t ; log2(10) mov [temp+8],edx fild [temp+8] ; x mov [temp+0],0 fmul ; z = x * log2(10) mov [temp+4],3F000000h ; 0.5 fld st fsub [temp+4] ; z - 0.5 fistp [temp+8] ; round(z) mov [temp+4],80000000h fisub [temp+8] ;z - round(z) f2xm1 ; 2^(mantissa)-1 add [temp+8],3FFFh ; 2^round(z) fld1 fadd ; add 1 and pop fld tbyte ptr temp fmul ; 2^((mantissa)*round(z)) jmp short @F ;_multiply ;-------------------------------------------------------- _load: shl edx, 2 ; edx = edx*4 fld [PowerTable12+edx+edx*2] ; edx = edx*12 ;-------------------------------------------------------- @@: fmul ; X * 10^expscaleS = d.?????? fld st ; get a copy fstp tbyte ptr realx xor edx, edx mov ebx, [realx+0] ;---------------------------------------------------------------- _getrealx: mov ecx, [realx+8] mov eax, [realx+4] ; and ecx, 7FFFh sub ecx, 16382 ; 10=> exponent = 16386 cmp ecx, 4 ja shift0X jmp [shifttableX+ecx*4] _new: mov eax, ebx;[realx+0] add eax,1 mul esi mov ebx, eax mov ecx, edx mov eax, [realx+4] mul esi add eax, ecx jmp _aa shift4X:: test eax, 60000000h ; 6= 0110B jz short a6 ; ------------------------ ; correct exponent * 10^-1 ; exponent = exponent + 1 ; ------------------------ fld [PowerTable12+12] ; = 0.1 fmul add iExp, 1 ; fld st ; NEW get a copy fstp tbyte ptr realx mov eax, [realx+4] mov ebx, [realx+0] jmp shift1X a6: shld edx, eax, 4 ;ecx=1 ebx+4 shld eax, ebx, 4 ;ecx=2 ebx+8 shl ebx, 4 ;ecx=3 ebx+9 add ebx, 12;round ;ecx=4 ebx+12 jmp shift0X ; ------------------------------- ; fraction bits in EAX:EBX ; shift: EDX <- EAX <- EBX ; ------------------------------- shift3X:: shld edx, eax, 3 shld eax, ebx, 3 shl ebx, 3 ; * 8 add ebx, 10 ;round jmp shift0X ; ------------------------------- ; fraction bits in EAX:EBX ; shift: EDX <- EAX <- EBX ; ------------------------------- shift2X:: shld edx, eax, 2 shld eax, ebx, 2 shl ebx, 2 ; * 4 add ebx, 8 ;round jmp shift0X ; ------------------------------- ; fraction bits in EAX:EBX ; shift: EDX <- EAX <- EBX ; ------------------------------- shift1X:: shld edx, eax, 1 shld eax, ebx, 1 shl ebx, 1 ; * 2 add ebx, 4 ;round ;------------------------------------------------------- ; ------------------------------- ; fraction bits in EDX:EAX:EBX ; ------------------------------- shift0X:: adc eax, 0 ; add carry to eax and edx _aa: adc edx, '0' ; ASCII convert ; mov dword ptr [edi+21],'00+E';ASCII chars for exponent mov dword ptr [edi+25],'00' mov [edi+1], dl ;first char in string ;------------------------------------------------ ; eax ebx ; 10 ; ---------------------------- ; ecx ebx ; edx eax ; ------------------------------ ; edx+carry eax ebx ;------------------------------------------------ k=3 REPEAT dp_1 mov ecx,eax ; save EAX mov eax,ebx mul esi ; edx:eax=ebx*10 mov ebx,eax mov eax,ecx ; restore EAX mov ecx,edx ; save EDX mul esi ; edx:eax=eax*10 add eax,ecx adc edx,'0' ; ASCII convert mov [edi+k], dl ; next char in string k=k+1 ENDM mov ecx,eax ; save EAX mov eax,ebx mul esi ; edx:eax=ebx*10 mov eax,ecx ; restore EAX mov ecx,edx ; save EDX mul esi ; edx:eax=eax*10 add eax,ecx adc edx,'0' ; ASCII convert mov eax, iExp mov [edi+k], dl ; last char in string add eax, aExp jz _exit ;if exponent=0 go to exit EXPONENTREAL_10KM _exit: mov edi,[esp+buffer] ;restore edi mov esi,[esp+buffer+4] ;restore esi mov ebx,[esp+buffer+8] ;restore ebx fstp st(0) ;remove realx add esp,buffer+12 ;remove buffer,locals xor eax, eax ;sucsess code retn 8 ;go to main program ;----------------------------------------------------------- _isinfinity: test ecx,ecx jns short _erro1 ; is NAN cmp ecx,80000000h jnz _other or ebx, ebx jne short _erro1 ; is NAN mov dword ptr [edi+1], "IFNI" mov dword ptr [edi+5], "YTIN" mov dword ptr [edi+9],0 xor eax, eax mov edi,[esp+buffer];pop edi mov esi,[esp+buffer+4];pop esi mov ebx,[esp+buffer+8];pop ebx add esp,buffer+12 ret 8 ;-------------------------- ; value to be converted = 0 ;-------------------------- _iszero: mov esi,[esp+buffer+4];pop esi mov dword ptr [edi+1],"0.0" mov ebx,[esp+buffer+8];pop ebx mov edi,[esp+buffer];pop edi add esp,buffer+12 xor eax, eax ret 8 ; -------------------- ; is indefinite or NAN ; -------------------- _erro1: mov eax, 1 mov dword ptr [edi+1], "ORRE" mov dword ptr [edi+5], "R" stc mov edi,[esp+buffer];pop edi mov esi,[esp+buffer+4];pop esi mov ebx,[esp+buffer+8];pop ebx add esp,buffer+12 ret 8 _other: test ecx,0BFFFFFFFh jnz _other1 mov dword ptr [edi+1], "ngiS" mov dword ptr [edi+5], "nila" mov dword ptr [edi+9], "aN g" mov dword ptr [edi+13],"N" xor eax, eax mov edi,[esp+buffer];pop edi mov esi,[esp+buffer+4];pop esi mov ebx,[esp+buffer+8];pop ebx add esp,buffer+12 ret 8 _other1: cmp ecx,0C0000000h jnz _Quit_NaN or ebx, ebx jne _Quit_NaN mov dword ptr [edi+1], "ednI" mov dword ptr [edi+5], "inif" mov dword ptr [edi+9], "et" xor eax, eax mov edi,[esp+buffer];pop edi mov esi,[esp+buffer+4];pop esi mov ebx,[esp+buffer+8];pop ebx add esp,buffer+12 ret 8 _Quit_NaN: mov dword ptr [edi+1], "eiuQ" mov dword ptr [edi+5], "aN t" mov dword ptr [edi+9], "N" xor eax, eax mov edi,[esp+buffer];pop edi mov esi,[esp+buffer+4];pop esi mov ebx,[esp+buffer+8];pop ebx add esp,buffer+12 ret 8 Eprst endp Программа для перевода REAL4 в строку Программа для перевода REAL8 в строку integer to float32 Код (ASM): .data x dw 1ABCh y dd 0 .code mov ax,[x] or ax,ax jz exit jns @f mov byte ptr [y+3],80h neg ax @@: mov cl,16 bsr bx,ax sub cl,bl shl ax,cl shr ax,1 add bx,127 shl bx,7 or word ptr [y+2],bx or word ptr [y+1],ax exit: integer to float64 Код (ASM): .data x dw -7935 y dq 0 .code mov ax,[x] or ax,ax jz exit jns @f mov byte ptr [y+7],80h neg ax @@: mov cl,16 bsr bx,ax sub cl,bl shl ax,cl add bx,1023 shld bx,ax,4 shl ax,4 or word ptr [y+6],bx mov word ptr [y+4],ax exit: integer to float80 Код (ASM): .data x dw 1EFFh y dt 0 .code mov ax,[x] or ax,ax jz exit jns @f mov byte ptr [y+9],80h neg ax @@: mov cl,15 bsr bx,ax sub cl,bl shl ax,cl add bx,16383 or word ptr [y+8],bx mov word ptr [y+6],ax exit: Использованы материалы: Чарльз Петцольд «Код. Тайный язык информатики» — М.: Издательско-торговый дом «Русская Редакция», 2001. — 512 с.: ил. Serrgio / HI-TECH «Введение в машинный код» (Архив WASM.RU) Кнут, Дональд Эрвин «Искусство программирования. Том 2: Получисленные алгоритмы», 3-е изд.: — М.: Издательско-торговый дом «Вильямс», 2003. — 832 с.: ил. Сергей Борисович Гашков «Системы счисления и их применение» Серия: «Математическое просвещение» Выпуск 29. М.: МЦНМО, 2004. — 52 с.: ил. Материалы с сайта http://klein.zen.ru/klein/hscool/online/base/b1s1/