Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Представление дробного числа в памяти, биты 
V
    Опции темы
oper54
  Дата 25.12.2006, 11:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 217
Регистрация: 13.5.2005

Репутация: 1
Всего: 1



Доборое время суток...
Вот интересно стало как вы глядят числа на низком уровне...
1. Как выглядит отрицательное число(по битам)
2. Как выглядит число с плавающей запятой(по битам)
 smile 
PM MAIL   Вверх
jonie
Дата 25.12.2006, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 15
Всего: 118



почитай стандарт представления чисел с плавающей точкой....скажем в MSDN все написано даже... да и поиск тоже даст результаты.
на вскидку могу вспомнить: (проверить стоит)
signed integer - старший бит - знаковый(1=="-",0=="+") все остальное - число.
в беззнаковом этот бит отдается под число.

float - 8 бит экспонента, 23 - мантисса, старший бит опять же знак
double - 11 бит экспонента, 52 - мантисса, старший бит знаковый.

........IEEE читай .......
ну или там хотяб (особо обращаем внимание на хранение экпоненты)...:
http://www.intuit.ru/department/se/pbmsu/2/2.html


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
takedo
Дата 25.12.2006, 13:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 501
Регистрация: 1.6.2005

Репутация: 1
Всего: 3



В конце концов посмотри по битам сам через маску:
Приведу пример для самого простого типа, а дальше по аналогии:
char data;
bool bits[8];

bits[0] = data&0x80;
bits[1] = data&0x40;
...

Ну и так далее. Единственное, что может быть наоборот старший и младший биты, но для твоей задачи это неважно. smile 



--------------------
я не гольфист - я хоккеист
PM MAIL   Вверх
W4FhLF
Дата 25.12.2006, 14:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 20
Всего: 121



Что-то в последнее время стала популярна эта тема, отпарвлять читать маны интела смысла нет, поэтому я сейчас постараюсь довольно доступно разъяснить эту тему.

Цитата

1. Как выглядит отрицательное число(по битам)


Отрицательное целое или с плавающей точкой? 
Целые числа делятся на отрицательные и положительные условно, в памяти никакого деления на положительные и отрицательные нет, допустим int(в 32х разрядной системе) допускает значения от -2147483648(0x80000000) до 2147483647(0x7FFFFFFF) или от -2^31 до 2^31 - 1. 
Отсюда следует, что например -1 запишется в памяти как 0xFFFFFFFF, -2 как 0xFFFFFFFE.
Давай убедимся в этом на практике и продизассемблируем такую программу:

Код

int b;
int main()
{
    b = -1;
    b = -2;
    b = -2147483647;
    printf("%d", b);
    getch();
    return 0;
}


Прежде всего нас интересуют первые три операции присвоения, компилятор их скомпилирует в машинный код, а переведя этот машинный код на язык ассемблера(продизассемблировав его) мы увидим такие эквивалентые, с точки зрения процессора, команды:

Код

OR DWORD PTR DS:[402008],FFFFFFFF // b = -1;
MOV DWORD PTR DS:[402008],FFFFFFFE // b = -2;
MOV DWORD PTR DS:[402008],80000001 // b = -2147483647;


Операция MOV DWORD PTR DS:[402008],FFFFFFFE на языке ассемблера дословно значит: присвоить ячейке памяти по смещению 0x402008 значение размером 4 байта(DWORD) равное 0xFFFFFFFE.
Каждую строку я закоментировал. В первой операции присвоения используется OR только потому, что она занимает меньше места, чем MOV, но она эквивалентна ей в данном случае.

Отсюда можно вывести простую закономерность, фактическое представление любого отрицательного целого числа в памяти можно получить по формуле:
RealValue = 0xFFFFFFFF - b + 1
Где b - это абсолютное значение отрицательного числа.

Для удобства в процессоры была включена специальная команда для этого - NEG.
Выдержка из книги Юрова:
Цитата

Применение: Команда NEG используется для формирования двоичного дополнения операнда в памяти или регистре. Она предполагает инвертирование всех разрядов операнда с поледующим сложением его с двоичной единицей. Если операнд отрицательные, то операция NEG над ним означает получение его модуля.


Команда эта так же используется компилятором при иневертировании положительного знака целых чисел:
Код

int b;
int main()
{
    b = 2147483647;
    b = -b;
    return 0;
}

Такой вот код компилятор преобразует в следующее машинное выражение:

Код

MOV DWORD PTR DS:[402008],7FFFFFFF // b = 2147483647;
NEG DWORD PTR DS:[402008]  // b = -b;


Как видишь, сначала происходит присвоение положительного числа, а после изменение знака специальной командой процессора.

Цитата

2. Как выглядит число с плавающей запятой(по битам)


Тут всё сложнее. Дело в том, что работой с flat point числами занимается арифметический сопроцессор - FPU. Он имеет свои команды, свои регистры(при этом основными являются регистры R0-R7, состовляющие основу модели сопроцессора - стек сопроцессора. Размерность каждого из этих регистров 80 бит, обрати внимание на это).
Набор функций и форматы данных с которыми работает сопроцессор описывает стандарт IEEE 754 (http://babbage.cs.qc.edu/IEEE-754/References.xhtml).
При этом формат данных с плавающей запятой делится на 3 группы:
Короткий
Длинный
Расширенный

Отличаются они между собой прежде всего разрядностью, первый занимает в памяти 4 байта, второй 8 байт, а третий 10. 
В общем случае вещественное число или число с плавающей точкой имеет следующую структуру в памяти:
[знак][экспонента][мантисса]
От размера экспоненты и мантиссы как раз зависит формат данных.

В С/С++ мы привыкли использовать два формата: float и double. Первый является коротким форматом представления вещественного числа(под экспоненту отводится 8 бит, под мантиссу 24), второй длинным(под экспоненты отводится 11 байт, под мантиссу 53). Под знак во всех форматах отводится 1 бит.

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

Скомпилируем такой пример и посмотрим как с ним работает процессор:
Цитата

float a;
int main()
{
    a = -7.770000;
    return 0;
}


Смотрим во что превращается команда присвоения:

Код

FLD DWORD PTR DS:[403094]
FSTP DWORD PTR DS:[402000] // a = -7.770000;


Ага, ну вот, то о чём я и говорил. Сопроцессор использует свой набор команд, поэтому непосвящённым они кажутся совсем непривычными. При этом в них нет ничего необычного. Первая команда загружет нв верхушку стека сопроцессора значение из памяти, а вторая кладёт значение с вершины по адресу 0x402000(очевидно, что это смещение в памяти нашей переменной a). 
При этом если мы посмотрим по адресу 0x403094 мы не увидим ничего даже отдалённо напоминающего -7.770000, вместо этого мы видим следующее значение по этому адресу: 0xC0F8A3D7. 
Вот это и есть представление числа  -7.770000 в памяти, таким его видит процессор. Основываясь на вышесказанном, конвертируем это число в последовательность битов и получаем:
11000000111110001010001111010111

Раскладывая его на составляющие получаем:
1 - знак. (0 - число положительное; 1 - отрицательное)
10000001 - Экспонента. Показывает сколько порядков надо взять у мантиссы, чтобы получить целую часть числа.
11110001010001111010111 - Мантисса.

Начнём постепенно строить модель нашего числа. Нам известно, что число отрицательное, теперь нужно перевести экспоненту в нормальный вид. Берём её значение, оно равно 129 и чтобы получить кол-во битов целой части отнимаем от этого числа 127, получаем 2(на самом деле устройство экспоненты это довольно обширная тема, я в подробности вдаваться не стал). Берём у мантиссы 2 бита в счёт целой части:
11.110001010001111010111

Но 11b это не 7, скажите вы, и будете правы. На самом деле перед этим я должен был сказать, что при преобразовании в число с плавающей точкой в мантиссу уходит всё до последнего бита, равного 1. Этот бит всегда есть и поэтому его нет смысла хранить в памяти, он добавляется в "уме". 
Вы должны были обратить на это внимание, когда я описывал формат и сказал, что размер мантиссы у float - 24 бита, а когда мы разделили на составляющие наше число получили 23 бита, перед этими 23 битами стоит ещё один. Учитывая это получаем:
111.110001010001111010111

Итак, целая часть 111b, а это как раз 7 в десятичной системе.

Осталось привести к нормальному виду дробную часть: 110001010001111010111, но и тут наши приключения не заканчиваются smile Дробная часть получается по следующей формуле(в случае, если Drob представить как массив бит и нумерация элементов массива начинается с 1):

DrobValue = Drob(1) * 2^(-1) + Drob(2) * 2^(-2) + Drob(3) * 2^(-3) + Drob(4) * 2^(-4) ... Drob(n) * 2^(-n)
Где n кол-во бит в оставшейся мантиссе.

Или, если применить к нашему случаю, получаем:

DrobValue = (1 * 2^(-1)) + (1 * 2^(-2)) + (0 * 2^(-3)) +  (0 * 2^(-4)) ... 1 * 2^(-21)

Если проссумировать всё, как написано выше, получим: 0,769999980926514

И вот тут открывается ещё одна тайнаsmile Хочу сразу сказать, что точность вычислений формата float ограничена 7 знаками после запятой, поэтому всё, что после не учитывается, хотя оно и не влияет на результат. Так всё-таки, почему так? Всё очень просто, при переводе десятичных дробей в двоичные мы получаем гораздо больше варинатов периодических дробей(В десятичных дробях знаменатели - это всегда степени десятки, в двоичных дробях знаменатели - это всегда степени двойки), отсюда и появляется некоторая погрешность. Она присутствует всегда и от неё никуда не деться. Как правило ЯВУ дают возможность абстрагироваться от неё округляя такие числа, поэтому конечному пользователю она не видна, однако на аппаратном уровне вычисления происходят с дробами, где эта погрешность всё-таки имеется.

Связывая в одно всё вышесказанное, значение 0xC0F8A3D7 превращается в отрицательное число с плавающей точкой, где целая часть равна 7, а дробная 769999980926514: 
-7.769999980926514



Ну вот, надо же... Целая "статья" получилась. На полноту описание конечно же не претендует, я и половины особенностей форматов FPU не рассмотрел, однако постарался изложить всё просто и доступно, подврепив примерами. Учите господа ассемблер и тогда для Вас всё будет прозрачным и понятным smile


Это сообщение отредактировал(а) W4FhLF - 25.12.2006, 14:36


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
oper54
Дата 16.1.2007, 21:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 217
Регистрация: 13.5.2005

Репутация: 1
Всего: 1



спасибо, исчерпывающий ответ...

предлагаю, запостить эту статью в FAQ

Это сообщение отредактировал(а) oper54 - 16.1.2007, 21:21
PM MAIL   Вверх
GIK
Дата 17.1.2007, 16:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Добрый человек
**


Профиль
Группа: Участник
Сообщений: 985
Регистрация: 3.6.2005
Где: я только не небыв ал

Репутация: 1
Всего: 14



Цитата

Отсюда можно вывести простую закономерность, фактическое представление любого отрицательного целого числа в памяти можно получить по формуле:
RealValue = 0xFFFFFFFF - b + 1

Помница на уроках информатики это рассуждали так:
Все нули становятся единицами, все единицы становятся нулями и прибавляется 1 (как число)
Например число 1 выглядит так (в одном байте)
00000001
Число -1 будет таким
11111111
т.е. изменение 1 на 0 и наоборот + один 
Так вроде легче понять начинающему  smile 
Поправте если не прав, а то я в это дело давно не влазил, хотя нужно знать хорошо smile 



--------------------
Математика=>пиво=> програмирование, три вещи последовательны и совместимы !!!
Программирование - это не деятельнось! Программирование - это состояние души!
Бог - самый крутой программист.
PM MAIL ICQ   Вверх
SerpentVV
Дата 17.1.2007, 20:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 52
Регистрация: 27.11.2006
Где: Астрахань

Репутация: 1
Всего: 1



Цитата(GIK @ 17.1.2007,  16:40)
Цитата

Отсюда можно вывести простую закономерность, фактическое представление любого отрицательного целого числа в памяти можно получить по формуле:
RealValue = 0xFFFFFFFF - b + 1

Помница на уроках информатики это рассуждали так:
Все нули становятся единицами, все единицы становятся нулями и прибавляется 1 (как число)
Например число 1 выглядит так (в одном байте)
00000001
Число -1 будет таким
11111111
т.е. изменение 1 на 0 и наоборот + один 
Так вроде легче понять начинающему  smile 
Поправте если не прав, а то я в это дело давно не влазил, хотя нужно знать хорошо smile

Да, это называется "дополнительный код".
От количества разрядов не зависит.
Например, для 8 разрядов (битов) на примере +1
+1 = 00000001
инвертируем
11111110
прибавляем 1 в младший разряд
11111111 = -1

Проверяем +1 -1 = 0
1|1111111                      единички переноса
  |00000001
  |11111111
----------------
   00000000 = 0

два числа не переводятся: 0 и самое маленькое отрицательное число...
для 8 битов это
10000000 = -128
Числа +128 нет, есть +127 = 
01111111 = +127
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1129 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.