Для глубокого понимания работы компьютера желательно знание того, как различные числовые величины представляются в компьютере. Это т.н. машинное представление чисел. С целыми числами всё достаточно просто. Если число положительное, то оно стандартным образом переводится в двоичное и полученные двоичные разряды записываются в байты, выделенные для данной переменной. При этом самый старший бит (самый левый) при любом представлении целого числа равен нулю или единице в зависимости от знака числа 0/1 +/-. Для получения машинного представления целого числа со знаком минус производит следующие действия:
Знак минус временно забывается.
Это положительное число стандартным образом переводится в двоичное представление. Это т.н. исходный код.
все разряды исходного кода инвертируются (взаимообмен нулей и единиц) и это дает обратный код.
к обратному коду добавляется 1 и это дает дополнительный код, этот дополнительный код является двоичной записью исходного отрицательного числа (старший бит числа равен 1).
С плавающими числами всё значительно сложнее.
Ниже приводится программа на языке С, переводящая введенное плавающее типа FLOAT в его машинное представление или, наоборот, машинное представление числа в его запись в различных системах счисления.
В самой программе даются подробные комментарии всех действий.
Далее приводится вариант программы на языке С (компилятор BORLAND C++ 3.1)
//******************************************************************* // // Программа работает в двух режимах: // - переводит введенное число в его машинное представление // в тип FLOAT (32 двоичных разряда) // - переводит машинное представление типа FLOAT в его // двоичное, десятичное, 8 и 16-ти ричные представления. // При переводе плавающего числа в машинное представление // типа FLOAT/DOUBLE/LONG DOUBLE следует помнить следующее: // - на всё число отводится 32/64/80 двоичных разряда // - разряды нумеруются справа налево от 0 до 31/63/79 // - самый старший разряд (31/63/79) определяет знак всего числа 0/1 +/- // - разряды 23-30/52-62/64-78 определяют характеристику увеличенную на // 127/1023/16383 // - сама характеристика определяется как смещение в двоичной // записи исходного плавающего числа, обеспечивающее (смещение) // перенесение десятичной точки сразу ЗА первой значащей единицей // для форматов FLOAT/DOUBLE. Для формата LONG DOUBLE - ПЕРЕД первой // значащей единицей. Т.е. для первых двух первая значащая единица // подразумевается, а для последнего - она имеется в машинной записи числа. // Если при этом переносил влево, характеристика=127/1023/16383+перенос, // а если вправо, то характеристика=127/1023/16383-перенос. // - в разрядах 0-22/0-51/0-63 хранится мантисса. // Вся информация по номерам используемых бит для // различным форматам собрана в таблице ниже // формат знак характеристика мантисса // FLOAT 31 30 - 23 +127 22 - 0 // DOUBLE 63 62 - 52 +1023 51 - 0 // LONG D.. 79 78 - 64 +16383 63 - 0 // Все примеры рассмотрены для формата FLOAT. // ПРИМЕР №1. // Необходимо получить машинное представление десятичного числа 45.56. // Его двоичное представление имеет вид // 101101.100011110101110001 // Для того, чтобы десятичная точка оказалась после первой значащей единицы // её надо переместить влево на 5 разрядов. Таким образом смещенная // характеристика будет равна 127+5=132=10000100. Это помещается в разряды // 23-30. В разряды 0-22 помещается смещенная мантисса, но без самой старшей // единицы, которая предполагается по умолчанию. Таким образом мантисса // будет иметь вид 01101100011110101110001. // Общая запись числа 45.56 в машинном представлении типа FLOAT имеет вид // + хара+127 мантисса без старшей единицы // 1 87654321 32109876543210987654321 // 0 10000100 01101100011110101110001 =42363D71 // Если последнее представление перевести в 16-ти ричное, то получим 42363D71. // См. Юров Ассемблер. стр. 515. // ПРИМЕР №2. // Необходимо получить машинное представление числа -0.123е-1. // Его двоичное представление (без учета знака) имеет вид // 0.000000110010011000010 // Для того, чтобы десятичная точка оказалась после первой значащей единицы // её надо переместить вправо на 7 разрядов. Таким образом смещенная // характеристика будет равна 127-7=120=01111000. Это помещается в разряды // 23-30. В разряды 0-22 помещается смещенная мантисса, но без самой старшей // единицы, которая предполагается по умолчанию. Таким образом характеристика // будет иметь вид 10010011000010111110000. // Общая запись числа -0.0123 в машинном представлении типа FLOAT имеет // вид (с учетом 1 в 31-ом разряде за счет минуса перед всем числом) // + хара+127 мантисса без старшей единицы // 1 87654321 32109876543210987654321 // 1 01111000 10010011000010111110000 = // Если последнее представление перевести в 16-ти ричное, то получим BC4985F0. //******************************************************************************************* #include<stdio.h> #include<conio.h> void s816(int); char rezdw[32]; // массив переписи двоичного результата в симв. виде char mss[11]; // массив символьной записи числа в 8 и 16 СС int har=0; // характеристика int k=0; int sn; // знак числа // Объявляю тип структуры с битовыми полями struct byte { unsigned b1:1; unsigned b2:1; unsigned b3:1; unsigned b4:1; unsigned b5:1; unsigned b6:1; unsigned b7:1; unsigned b8:1; }; // Глобальная переменная, определяющая номер выводимого байта int nb; // Объявляю тип объдинение и переменную этого типа. // Тут обе составляющие занимают одно и то же место (поочереди). // Тут это необходимо по той причине, что побитовые операции // допустимы только с целыми. union bits { char ch; struct byte b; }u; // объединение, совмещающее FLOAT и LONG INT union sl { float fl; long int lf; }u1; // Прототипы функций. void decode (union bits ); void vivod (long int); //************************ // Основная программа. //************************ void main (void) { float b1=0,bb=1; char ch1; char a[9]; // массив ввода исходного числа в символьном виде int b[9]; // массив перевода символов в их числовые значения unsigned long int mant; // мантисса unsigned long int wes=1; // вес int dw[31]; // массив записи двоичных разрядов мантиссы int i,j=0,har1; int pr=0; clrscr(); printf("nУкажите режим работы ПЛАВ.->МАШИН. / МАШИН.->ПЛАВ. / ВЫХОД 0/1/не 0 и не 1 "); scanf("%d",&pr); while ((pr==0) || (pr==1)) { if (pr==0) { //================================================ // перевод плавающего в машинное представление //================================================ printf("nВведите анализируемое плавающее число "); scanf("%f",&u1.fl); printf("Число=%18.8f имеет двоичную запись n",u1.fl); printf("+ хара+127 мантисса без старшей единицыn"); printf("1 87654321 32109876543210987654321n"); vivod(u1.lf); getch(); } else //================================================ // перевод машинного представления в плавающее //================================================ { b1=har=k=j=0; wes=bb=1; puts("nВведите анализируемое число в машинном представлении "); gets(a); gets(a); // начальное обнуление for(i=0;i<=30;dw[i++]=0); for(i=0;i<=31;rezdw[i++]='\0'); // цикл перевода символов числа в их значение for(i=0;i<=8;i++) { if ((a[i]>=48) && (a[i]<=57 )) b[i+1]=a[i]-48; if ((a[i]>=97) && (a[i]<=102)) b[i+1]=a[i]-87; if ((a[i]>=65) && (a[i]<=70 )) b[i+1]=a[i]-55; } // определяю знак числа sn=(b[1]>7)?(1,b[1]-=8):(0); mant=0; for(i=4;i<=8; i++) { mant=(b[i]+mant)*16; wes*=16; } mant/=16; if (b[3]>7) { b[3]-=8; har=1; } mant+=b[3]*wes; har+=b[2]*2; har+=b[1]*32; har-=127; // получил истинное значение ха-ки (без сдвига на 127) // перевод мантиссы в ее двоичную запись for(i=30;i>=1;i--) { dw[i]=mant%2; mant/=2; } dw[7]=1; // Вывод результата во всех системах счисления. //************************ // ДВОИЧНОЕ ПРЕДСТАВЛЕНИЕ. //************************ printf("nДВОИЧНОЕ ПРЕДСТАВЛЕНИЕ "); if (sn) printf("-"); else printf("+"); if (har>=0) for(i=7;i<=30;i++) { if (j==har+1) { printf("."); rezdw[j++]='.'; } printf("%d",dw[i]); rezdw[j++]=dw[i]+48; } else { har1=-har; printf("0."); rezdw[j++]='0',rezdw[j++]='.'; for(i=1;i<har1;printf("0"),rezdw[j++]='0',i++); for(i=7;i<=30-har1;i++) { printf("%d",dw[i]); rezdw[j++]=dw[i]+48; } } //*********************************************************** // ДЕСЯТИЧНОЕ ПРЕДСТАВЛЕНИЕ //*********************************************************** i=0; while (rezdw[i++]!='.') {bb*=2;k++;}; for(i=0;rezdw[i];b1+=((rezdw[i] != '.')?((rezdw[i]-48)*(bb/=2)):(0)),i++); printf("nДЕСЯТИЧНОЕ ПРЕДСТАВЛЕНИЕ "); if (sn) printf("-"); else printf("+"); printf("%-18.8f",b1); printf("nВОСЬМЕРИЧНОЕ ПРЕДСТАВЛЕНИЕ "); s816(16); printf("nШЕСТНАДЦАТИРИЧНОЕ ПРЕДСТАВЛЕНИЕ "); s816(8); getch(); } if ((pr!=0) && (pr!=1)) break; printf("nУкажите режим работы ПЛАВ.->МАШИН. / МАШИН.->ПЛАВ. / ВЫХОД 0/1/не 0 и не 1 "); scanf("%d",&pr); } } // Функция двоичной расшифровки байта. void decode (union bits b) { // Печатаю последовательно от левого к правому значения битов. // В обращении b.b.bn: // первая b - имя объединения // вторая b - имя структуры в объединении // bn - имя поля в структуре printf("%d",b.b.b8); if (nb<=2) printf(" "); printf("%d",b.b.b7); printf("%d",b.b.b6); printf("%d",b.b.b5); printf("%d",b.b.b4); printf("%d",b.b.b3); printf("%d",b.b.b2); printf("%d",b.b.b1); } // Функция печати значений бит 32-х разрядного LONG INT. // Печатает побайтно слева направо вплотную. void vivod(long int a) { unsigned char uu[5]; // вывод старшего байта uu[nb=1]=u.ch=a>>24; decode(u); // вывод второго слева байта uu[nb=2]=u.ch=(a>>16)&255; decode(u); // вывод третьего слева байта uu[nb=3]=u.ch=(a>>8)&255; decode(u); // вывод последнего (правого) байта uu[nb=4]=u.ch=a&255; decode(u); printf(" ="); // вывод исходного значения в 16-ти виде побайтно. for(int i=1;i<=4;i++) { (uu[i]<16)? printf("0"):(0); printf("%X",uu[i]); } } //***************************************************** // Функция перевода символьного представления двоичного // плавающего в 8-ми и 16-ти системы счисления. //***************************************************** void s816(int ss) { int j=4 ,kk,jj; int i,n, ii,sum; // опробеливаю массив вывода результата for(i=0;i<11;mss[i++]=' '); // Запись в массив результата десятичной точки и знака числа mss[0]=((sn)?('-'):('+')); if (ss==8) j--; n=(k+j-1)/j+1; mss[n]='.'; // обработка целой части jj=k-1; for(i=n-1;i>=1;i--) { for(ii=1,sum=0 ,kk=1;ii<=j && jj>=0;ii++) { sum+=(rezdw[jj--]-48)*kk; kk*=2; } mss[i]=sum+((sum<=9)? 48:55); } // обработка дробной части jj=k+1; for(i=n+1;i<=31;i++) { kk=((ss==16)? (8):(4)); for(ii=1,sum=0;ii<=j && rezdw[jj]!='\0';ii++) { sum+=((rezdw[jj++]-48)*kk); kk/=2; } if (rezdw[jj]=='\0') break; mss[i]=sum+((sum<=9)? 48:55); } for(ii=0;ii<=i;printf("%c",mss[ii++])); }