Оскільки 32-бітова система не може керувати числом 2 ^ 33 (оскільки очевидна 32-бітна межа), як можна керувати 80-бітним числом з плаваючою точкою ?
Він повинен вимагати "80-бітового" ...
Оскільки 32-бітова система не може керувати числом 2 ^ 33 (оскільки очевидна 32-бітна межа), як можна керувати 80-бітним числом з плаваючою точкою ?
Він повинен вимагати "80-бітового" ...
Відповіді:
Одне із значень 32-бітного процесора полягає в тому, що його регістри мають 32 біти. Це не означає, що він не може мати справу з, скажімо, 64-бітовими числами, просто що він повинен мати справу з нижньою 32-бітовою половиною спочатку, а потім з верхньою 32-бітовою половиною секунди. (Тому CPU має прапор перенесення .) Це повільніше, ніж якби CPU міг просто завантажити значення в ширший 64-розрядний реєстр, але все-таки це можливо.
Таким чином, "розрядність" системи не обов'язково обмежує розмір чисел, з якими може мати справу програма, тому що ви завжди можете розбивати операції, які не вписуються в регістри процесора на кілька операцій. Так це робить операції повільнішими, споживає більше пам’яті (якщо вам доведеться використовувати пам’ять як «скретчпад»), і складніше програмувати, але операції все ж можливі.
Однак нічого з цього не стосується, наприклад, процесорів Intel 32 біт і плаваючої точки, оскільки частина процесора з плаваючою комою має власні регістри, і вони мають 80 біт шириною. (На початку історії x86 можливість з плаваючою комою була окремою мікросхемою, вона була інтегрована в процесор, починаючи з 80486DX.)
@ Відповідь прориву надихнула мене додати це.
Значення з плаваючою комою, наскільки вони зберігаються в регістрах FPU, працюють зовсім інакше, ніж двійкові цілі числа.
80 біт значення величини з плаваючою комою поділяються на мантісу та експонент (також існує "база" у числах з плаваючою комою, яка завжди дорівнює 2). Мантіса містить значущі цифри, і показник визначає, наскільки великі ці значущі цифри. Таким чином, немає "переповнення" в інший реєстр, якщо ваше число стає занадто великим, щоб вміститися в мантісі, ваш показник збільшується, і ви втрачаєте точність, тобто коли перетворите його на ціле число, ви втратите десяткові знаки справа від вас - тому його називають плаваючою точкою.
Якщо ваш показник занадто великий, то у вас є переповнення з плаваючою комою, але ви не можете легко перенести його на інший регістр, оскільки експонент і мантіса пов'язані між собою.
Я міг би бути неточним і неправильним щодо цього, але я вважаю, що це суть цього. (Ця стаття у Вікіпедії ілюструє вищезазначене трохи більш лаконічно.)
Це нормально, що це працює зовсім по-іншому, оскільки вся частина процесора з плаваючою комою є свого роду у власному світі - ви використовуєте спеціальні інструкції CPU для доступу до цього тощо. Крім того, до суті питання, тому що це окремо, біт FPU не є щільно поєднаний з розрядністю рідного процесора.
-fomit-frame-pointer
для повернення цього реєстру.
32-розрядні, 64-бітні та 128-бітні всі відносять до довжини слова процесора, яку можна вважати "основним типом даних". Часто це кількість бітів, переданих в / з оперативної пам’яті системи, і ширина покажчиків (хоча ніщо не заважає використовувати програмне забезпечення для отримання більшої кількості оперативної пам’яті, ніж те, що може отримати доступ до одного вказівника).
Якщо припустити постійну тактову частоту (як і все інше в архітектурі, яка є постійною), і якщо припускати, що читання / запис пам'яті є однаковою швидкістю (ми припускаємо тут 1 тактовий цикл, але це далеко не так у реальному житті), ви можете додайте два 64-розрядних числа за один тактовий цикл на 64-бітній машині (три, якщо рахувати отримання чисел з ОЗУ):
ADDA [NUM1], [NUM2]
STAA [RESULT]
Ми також можемо робити те саме обчислення на 32-розрядній машині ... Однак на 32-розрядній машині нам це потрібно зробити в програмному забезпеченні, оскільки спочатку потрібно додати нижні 32-бітні компенсації переповнення, а потім додати верхні 64-бітні:
ADDA [NUM1_LOWER], [NUM2_LOWER]
STAA [RESULT_LOWER]
CLRA ; I'm assuming the condition flags are not modified by this.
BRNO CMPS ; Branch to CMPS if there was no overflow.
ADDA #1 ; If there was overflow, compensate the value of A.
CMPS ADDA [NUM1_UPPER], [NUM2_UPPER]
STAA [RESULT_UPPER]
Переглядаючи мій синтаксис складеної збірки, ви легко бачите, як операції з високою точністю можуть зайняти експоненціально довший час на машині з меншою довжиною слова. Це справжній ключ для 64-бітних і 128-бітних процесорів: вони дозволяють нам обробляти більшу кількість бітів за одну операцію. Деякі машини містять інструкції щодо додавання інших величин із перенесенням (наприклад, ADC
на x86), але наведений вище приклад має на увазі довільні значення точності.
Тепер, щоб поширити це на питання, просто зрозуміти, як ми могли б додати числа, більші за наявні у нас регістри - ми просто розбиваємо проблему на шматки розмірів регістрів і працюємо звідти. Хоча як зазначає @MatteoItalia , стек F87 x87 має вбудовану підтримку 80-бітових величин, у системах, яким не вистачає цієї підтримки (або процесорам, у яких повністю не вистачає одиниці з плаваючою комою!), Еквівалентні обчислення / операції повинні виконуватися в програмному забезпеченні .
Отже, для 80-бітного числа після додавання кожного 32-розрядного сегмента також слід перевірити наявність переливу в 81-й біт і, можливо, нульовий біт вищого порядку. Ці перевірки / нулі виконуються автоматично для певних інструкцій x86 та x86-64, де вказані розміри вихідного та призначення операндів (хоча вони вказані лише в потужностях 2, починаючи з ширини 1 байт).
Звичайно, з числами з плаваючою комою не можна просто виконати двійкове додавання, оскільки мантіса та значні цифри упаковуються разом у зміщенному вигляді. В ALU на процесорі x86 є апаратна схема, яка виконує це для 32-розрядних і 64-розрядних поплавків IEEE; однак , навіть за відсутності блоку з плаваючою комою (FPU), однакові обчислення можуть проводитися в програмному забезпеченні (наприклад, за допомогою використання Наукової бібліотеки GNU , яка використовує FPU при компілюванні з архітектури, повертаючись до програмних алгоритмів якщо немає апаратного забезпечення з плаваючою комою (наприклад, для вбудованих мікроконтролерів, у яких відсутні FPU).
Враховуючи достатню кількість пам’яті, можна також виконувати обчислення за кількістю довільної (або «нескінченної» - в реалістичних межах) точності, використовуючи більше пам'яті, оскільки потрібна більша точність. Одна реалізація цього існує в бібліотеці множинної точності GNU , що дозволяє необмежену точність (звичайно, поки ваша ОЗУ не заповнена, звичайно) на цілі, раціональні та операції з плаваючою комою.
Архітектура пам'яті системи може дозволяти вам переміщати 32 біти одночасно, але це не заважає використовувати більші числа.
Подумайте про множення. Можливо, ви знаєте свої таблиці множення розміром до 10x10, але ви, мабуть, не маєте проблем із виконанням 123x321 на папері: ви просто розбиєте його на безліч дрібних проблем, помноживши окремі цифри та піклуючись про перенесення тощо.
Процесори можуть робити те саме. У "старі часи" у вас були 8-бітні процесори, які могли робити математику з плаваючою комою. Але вони були slooooooow.
"32-розрядна" - це дійсно спосіб категоризації процесорів, а не постановка на встановлення каменю. "32-бітний" процесор, як правило, має 32 бітні регістри загального призначення для роботи.
Однак у камені не встановлено вимоги, щоб все в процесорі було виконано в 32-розрядному. Наприклад, невідомо, що для "32-розрядного" комп'ютера мати 28-бітну шину адреси, оскільки це дешевше зробити апаратне забезпечення. 64-бітні комп'ютери часто мають лише 40-бітну або 48-бітну шину пам'яті з тієї ж причини.
Арифметика з плаваючою комою - ще одне місце, де розміри змінюються. Багато 32-бітних процесорів підтримували 64-бітні числа з плаваючою комою. Вони зробили це, зберігаючи значення плаваючої точки у спеціальних регістрах, які були ширшими, ніж регістри загального призначення. Щоб зберігати одне з цих великих чисел з плаваючою комою у спеціальних регістрах, слід спершу розділити число на два регістри загального призначення, а потім видавати інструкцію поєднувати їх у поплавок у спеціальних регістрах. Опинившись у цих регістрах з плаваючою точкою, значеннями будуть маніпулювати як 64-бітні плавці, а не як пара 32-бітних половинок.
80-бітна арифметика, яку ви згадуєте, є окремим випадком цього. Якщо ви працювали з числами з плаваючою комою, вам відома неточність, яка виникає внаслідок проблем із округленням плаваючої точки. Одне рішення для заокруглення - мати більше біт точності, але тоді вам доведеться зберігати більшу кількість і змушувати розробників використовувати в пам'яті незвично великі значення з плаваючою комою.
Рішення Intel полягає в тому, що регістри плаваючої точки - це 80 біт, але вказівки щодо переміщення значень до / з цих регістрів первинно працюють з 64-бітовими числами. Поки ви повністю працюєте в стеку з плаваючою крапкою Intel x87, всі ваші операції виконуються з 80 бітами точності. Якщо ваш код повинен витягнути одне з цих значень із регістрів плаваючої точки і зберегти його десь, він скорочує його до 64-біт.
Мораль історії: категоризації на кшталт "32-розрядні" завжди туманніші, коли заглиблюєшся в речі!
"32-бітний" процесор - це той, де більшість регістрів даних - це 32-бітні регістри, і більшість інструкцій діють на даних цих 32-бітних регістрів. 32-бітний процесор також може одночасно передавати дані в 32-бітову пам'ять і з неї. Більшість регістрів 32-розрядні не означає, що всі регістри є 32-бітовими. Коротка відповідь полягає в тому, що 32-розрядний процесор може мати деякі функції, які використовують інші бітові рахунки, такі як 80-бітні регістри з плаваючою точкою та відповідні інструкції.
Як заявив @spudone у коментарі до відповіді @ ultrasawblade, першим процесором x86, який інтегрував операції з плаваючою комою, був Intel i486 (конкретно, 80486DX, але не 80486SX), який, відповідно до сторінки 15-1 мікропроцесорних програмістів i486 Довідковий посібник включає до своїх числових регістрів «Вісім індивідуально адресованих 80-бітних цифрових регістрів». I486 має 32-бітну шину пам'яті, тому для перенесення 80-бітного значення знадобиться 3 операції з пам'яттю.
У попередника покоління 486, i386, не було інтегрованих операцій з плаваючою комою. Натомість він мав підтримку використання зовнішнього плаваючого пункту "співпроцесор" 80387. Цей копроцесор мав майже таку ж функціональність, що була вбудована в i486, як ви бачите на сторінці 2-1 Посібника з програміста 80387 .
80-бітний формат з плаваючою точкою, схоже, зародився з 8087, математичним співпроцесором для 8086 і 8088. 8086 і 8088 були 16-бітними процесорами (з 16-бітною і 8-бітовою шиною пам'яті), і все ще були в змозі використовувати 80-бітний формат з плаваючою точкою, скориставшись 80-бітовими регістрами у співпроцесорі.