Чи повинен я використовувати подвійний або плаваючий?


89

Які переваги та недоліки використання одного замість іншого в C ++?


Хто-небудь пробував створити масив з поплавками та масив з подвійних і перевірити, чи справді між членами на поплавцях є 4 байти, а між подвійними - 8 байт? Цілком можливо, що 64-бітний компілятор / комп’ютер все одно може зарезервувати 8 байт на члена для плаваючих елементів, хоча їм не потрібно так багато.
user3015682

Відповіді:


104

Якщо ви хочете знати справжню відповідь, вам слід прочитати те, що повинен знати кожен асистент з питань арифметики з плаваючою крапкою .

Коротше кажучи, хоча і doubleдозволяє підвищити точність у своєму поданні, для певних обчислень це призведе до більших помилок . «Правильним» вибором є: використовуйте стільки точності, скільки вам потрібно, але не більше, і вибирайте правильний алгоритм .

Багато компіляторів у будь-якому випадку роблять розширену математику з плаваючою комою в "несуворому" режимі (тобто використовують ширший тип з плаваючою комою, доступний в апаратному забезпеченні, наприклад, 80-бітові та 128-бітові плаваючі), це також слід враховувати. На практиці ви навряд чи можете помітити різницю в швидкості - вони все одно є вихідцями з обладнання.


10
Так. Завдяки сучасним центральним процесорам, які попередньо отримують все більші та більші фрагменти пам’яті, паралельних числових блоків обробки та конвеєрних архітектур, проблема швидкості дійсно не є проблемою. Якщо ви маєте справу з величезною кількістю цифр, ніж, можливо, різниця в розмірі між 4-байтним плаваючим і 8-байтним подвійним може змінити розмір пам'яті.
lavinio 02

5
Ну SSE (або будь-яка одиниця з плаваючою комою вертора) зможе обробити подвоєну кількість провалів за одну точність порівняно з подвійною точністю. Якщо ви робите лише x87 (або будь-який скаляр) з плаваючою комою, то це, мабуть, не буде мати значення.
Грег Роджерс 02

1
@Greg Rogers: компілятори зараз не такі розумні. Якщо ви не пишете сирі збірки, вони не мають великих відмінностей. І так, це може змінитися в міру еволюції компілятора.
J-16 SDiZ

Додаткові примітки: Якщо ви абсолютно не уявляєте, як виглядають дані (або просто не маєте уявлення на всю математику у посиланнях), просто використовуйте double- це в більшості випадків безпечніше.
J-16 SDiZ

2
Ця стаття не коротка.
jokoon

44

Якщо у вас немає певних причин вчинити інакше, використовуйте подвійний.

Можливо, що дивно, але саме подвійний, а не плаваючий тип є "звичайним" типом з плаваючою комою в C (і C ++). Стандартні математичні функції, такі як гріх і журнал, беруть дублі в якості аргументів і повертають дублі. Звичайний літерал із плаваючою комою, як, наприклад, коли ви пишете 3.14 у програмі, має тип double. Не плавати.

На типових сучасних комп'ютерах подвоєння може бути настільки ж швидким, як плаваючий, або навіть швидшим, тому продуктивність, як правило, не є фактором, який слід враховувати, навіть для великих розрахунків. (І це повинні бути великі обчислення, або продуктивність навіть не повинна входити вам у голову. Мій новий настільний комп’ютер i7 може зробити шість мільярдів множень вдвічі за одну секунду.)


26

Відповісти на це питання неможливо, оскільки в ньому немає контексту. Ось кілька речей, які можуть вплинути на вибір:

  1. Реалізація компілятора з поплавками, парними та довгими парними. Стандарт С ++ говорить:

    Існує три типи з плаваючою комою: плаваюча, подвійна та довга подвійна. Тип double забезпечує принаймні таку саму точність, як float, а тип long double забезпечує щонайменше стільки ж точності, як і double.

    Отже, усі три можуть мати однаковий розмір у пам’яті.

  2. Наявність FPU. Не всі центральні процесори мають FPU, і іноді типи з плаваючою комою емулюються, а іноді типи з плаваючою комою просто не підтримуються.

  3. Архітектура ФПУ. FPU IA32 внутрішньо 80-бітний - 32-бітні та 64-бітні плаваючі пристрої розширюються до 80-бітових при завантаженні та зменшуються при зберіганні. Існує також SIMD, який може робити чотири 32-бітові плаваючі параметри або два 64-бітові плаваючі паралельно. Використання SIMD не визначене в стандарті, тому для цього потрібен компілятор, який робить більш складний аналіз, щоб визначити, чи можна використовувати SIMD, або вимагає використання спеціальних функцій (бібліотек або власних даних). Результатом 80-бітного внутрішнього формату є те, що ви можете отримати дещо різні результати залежно від того, як часто дані зберігаються в оперативній пам'яті (таким чином, втрачаючи точність). З цієї причини компілятори не особливо оптимізують код з плаваючою комою.

  4. Пропускна здатність пам'яті. Якщо для подвійного потрібно більше місця для зберігання, ніж для плаваючого, читання даних займе більше часу. Це наївна відповідь. На сучасному IA32 все залежить від того, звідки беруться дані. Якщо це в кеш-пам'яті L1, навантаження незначне, якщо дані надходять з одного рядка кешу. Якщо воно охоплює більше одного рядка кешу, є невеликі накладні витрати. Якщо це з L2, це займає деякий час довше, якщо це в оперативній пам'яті, то це ще довше і, нарешті, якщо це на диску, це величезний час. Тож вибір float або double є менш важливим, ніж спосіб використання даних. Якщо ви хочете зробити невеликий розрахунок для безлічі послідовних даних, кращий невеликий тип даних. Багато обчислень на невеликому наборі даних дозволить вам використовувати більші типи даних із будь-яким значним ефектом. Якщо ви' при повторному доступі до даних дуже випадково, тоді вибір розміру даних є неважливим - дані завантажуються в сторінки / рядки кешу. Отже, навіть якщо вам потрібен лише байт з оперативної пам'яті, ви можете отримати 32 передані байти (це дуже залежить від архітектури системи). Крім усього цього, CPU / FPU може бути суперскалярним (він же конвеєрний). Отже, навіть незважаючи на те, що навантаження може зайняти кілька циклів, CPU / FPU може бути зайнятий чимось іншим (наприклад, множенням), що приховує час завантаження до певної міри.

  5. Стандарт не застосовує жодного конкретного формату для значень з плаваючою комою.

Якщо у вас є специфікація, це допоможе вам вибрати оптимальний варіант. В іншому випадку, це не означає, що використовувати.


16

Double є більш точним, але кодується на 8 байтів. float - це всього 4 байти, тому менше місця і менша точність.

Ви повинні бути дуже обережними, якщо у вашому додатку є дубль та плаваюча версія. У мене була помилка через це в минулому. Одна частина коду використовувала float, а решта коду використовувала double. Копіювання double в float, а потім float в double може спричинити помилку точності, яка може мати великий вплив. У моєму випадку це була хімічна фабрика ... сподіваємось, це не мало суттєвих наслідків :)

Я думаю, що саме через цю помилку ракета Ariane 6 вибухнула кілька років тому !!!

Подумайте добре про тип, який буде використовуватися для змінної


3
Зверніть увагу, що 4/8 байт для float / double навіть не гарантується, це буде залежати від платформи. Це може бути навіть той самий тип ...
sleske

2
Код Ariane 5 намагався перетворити 64-бітну плаваючу крапку, значення якої перевищувало 32 767, у ціле число зі знаком 16 біт. Це спричинило виняток із переповнення, через який ракета ініціювала свою послідовність самознищення. Кодом, про який йде мова, був код, який був повторно використаний зі старої меншої ракети.
cmwt

5

Я особисто постійно йду на подвійне, поки не побачу вузькі місця. Тоді я розглядаю можливість переходу на плаваючу позицію або оптимізацію якоїсь іншої частини


4

Це залежить від того, як компілятор реалізує double. Це дозволено, щоб double і float були однаковими (і це є в деяких системах).

З огляду на це, якщо вони справді різні, головним питанням є точність. Подвійний має набагато вищу точність завдяки різниці в розмірах. Якщо числа, які ви використовуєте, зазвичай перевищують значення плаваючого числа, тоді використовуйте подвійне.

Кілька інших людей згадували питання про ефективність. Це було б останнє в моєму списку міркувань. Правильність повинна бути вашим фактором No1.


3

2

Думаю, незалежно від відмінностей (на які, як зазначають усі, поплавці займають менше місця і взагалі швидше) ... хто-небудь коли-небудь страждає на проблеми продуктивності за допомогою подвійного? Я кажу, використовуйте double ... і якщо згодом ви вирішите "нічого собі, це дійсно повільно" ... знайдіть вузьке місце у вашій продуктивності (що, мабуть, не факт, що ви використовували double). ПОТІМ, якщо це все ще занадто повільно для вас, подивіться, де ви можете пожертвувати деякою точністю і скористатися плаваючою системою.



1

Це дуже залежить від центрального процесора, найбільш очевидні компроміси між точністю та пам'яттю. З ГБ оперативної пам'яті пам’ять не становить великих проблем, тому, як правило, краще використовувати doubles.

Що стосується продуктивності, то вона сильно залежить від центрального процесора. floats зазвичай отримує кращу продуктивність, ніж doubles, на 32-бітній машині. На 64 бітах doubles іноді швидші, оскільки це (зазвичай) власний розмір. Тим не менш, що буде мати значення набагато більше, ніж ваш вибір типів даних, - чи можна скористатися перевагами інструкцій SIMD на своєму процесорі чи ні.


0

double має вищу точність, тоді як плаваючі забирають менше пам'яті і швидші. Як правило, слід використовувати флоат, якщо у вас немає випадку, коли він недостатньо точний.


5
На типових сучасних комп'ютерах подвійний настільки ж швидкий, як і float.
Томас Падрон-Маккарті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.