Яка різниця між поплавком і подвійним?


420

Я читав про різницю між подвійною точністю та одиничною точністю. Тим НЕ менше, в більшості випадків, floatі , doubleздається, бути взаємозамінними, тобто з допомогою одного або іншого , здається, не впливають на результати. Це справді так? Коли поплавці та двійники взаємозамінні? Які відмінності між ними?

Відповіді:


521

Величезна різниця.

Як випливає з назви, a doubleмає вдвічі більшу точність [1] . Загалом a має 15 десяткових цифр точності, тоді як має 7.floatdoublefloat

Ось як обчислюється кількість цифр:

doubleмає 52 біти мантіси + 1 прихований біт: log (2 53 ) ÷ log (10) = 15,95 цифр

floatмає 23 біти мантіси + 1 прихований біт: log (2 24 ) ÷ log (10) = 7,22 цифри

Ця втрата точності може призвести до накопичення більших помилок укорочення при повторних обчисленнях, наприклад

float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.7g\n", b); // prints 9.000023

поки

double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.15g\n", b); // prints 8.99999999999996

Крім того, максимальне значення float становить приблизно 3e38, але подвійне - приблизно 1.7e308, тому використання floatможе вражати "нескінченність" (тобто спеціальне число з плаваючою комою) набагато простіше, ніж doubleдля чогось простого, наприклад, обчислення фактора 60.

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


Звичайно, іноді навіть doubleне досить точно, тому ми інколи long double[1] (вищенаведений приклад дає 9.000000000000000066 на Mac), але всі типи з плаваючою комою страждають від помилок округлення , тому якщо точність дуже важлива (наприклад, гроші обробка) ви повинні використовувати intабо клас дробів.


Крім того, не використовуйте +=для підсумовування безлічі цифр з плаваючою комою, оскільки помилки накопичуються швидко. Якщо ви використовуєте Python, використовуйте fsum. В іншому випадку спробуйте реалізувати алгоритм підсумовування Кахана .


[1]: З і С ++ стандарти не визначають відображення float, doubleі long double. Цілком можливо, що всі три реалізовані як подвійна точність IEEE. Проте, для більшості архітектур (GCC, MSVC; x86, x64, ARM) float є дійсно IEEE одинарної точності з плаваючою комою (binary32), і double це IEEE подвійної точності з плаваючою комою (binary64).


9
Звичайна порада підсумовування - сортувати свої числа з плаваючою комою за величиною (найменшою першою) перед підбиттям підсумків.
R .. GitHub СТОП ДОПОМОГАТИ

Зауважте, що хоча C / C ++ float і double майже завжди є IEEE однократною та подвійною точністю, відповідно C / C ++ довгий подвійний, набагато більш мінливий залежно від вашого процесора, компілятора та ОС. Іноді це те саме, що подвійне, іноді це якийсь розширений для системи формат, іноді це квадратна точність IEEE.
plugwash

@ R..GitHubSTOPHELPINGICE: чому? Чи можете ви пояснити?
допитливий

@ InQusitive: Розглянемо, наприклад, масив, що складається зі значення 2 ^ 24, а потім 2 ^ 24 повторень значення 1. Підсумовуючи по порядку, виходить 2 ^ 24. Реверсування виробляє 2 ^ 25. Звичайно, ви можете навести приклади (наприклад, зробити 2 ^ 25 повторень 1), коли будь-який порядок виявляється катастрофічно неправильним з одним акумулятором, але найменший за величиною перший є кращим серед таких. Для кращого вам потрібне якесь дерево.
R .. GitHub СТОП ДОПОМОГАЙТЕ

56

Ось що кажуть стандартні стандарти C99 (ISO-IEC 9899 6.2.5 §10) або C ++ 2003 (ISO-IEC 14882-2003 3.1.9 §8):

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

Стандарт C ++ додає:

Представлення значення типів з плаваючою комою визначено реалізацією.

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


27

Дано квадратичне рівняння: x 2  - 4.0000000  x  + 3.9999999 = 0, точні корені до 10 значущих цифр, r 1  = 2.000316228 і r 2  = 1.999683772.

Використовуючи floatі double, ми можемо написати програму тестування:

#include <stdio.h>
#include <math.h>

void dbl_solve(double a, double b, double c)
{
    double d = b*b - 4.0*a*c;
    double sd = sqrt(d);
    double r1 = (-b + sd) / (2.0*a);
    double r2 = (-b - sd) / (2.0*a);
    printf("%.5f\t%.5f\n", r1, r2);
}

void flt_solve(float a, float b, float c)
{
    float d = b*b - 4.0f*a*c;
    float sd = sqrtf(d);
    float r1 = (-b + sd) / (2.0f*a);
    float r2 = (-b - sd) / (2.0f*a);
    printf("%.5f\t%.5f\n", r1, r2);
}   

int main(void)
{
    float fa = 1.0f;
    float fb = -4.0000000f;
    float fc = 3.9999999f;
    double da = 1.0;
    double db = -4.0000000;
    double dc = 3.9999999;
    flt_solve(fa, fb, fc);
    dbl_solve(da, db, dc);
    return 0;
}  

Запуск програми дає мені:

2.00000 2.00000
2.00032 1.99968

Зауважте, що цифри не великі, але все одно ви отримуєте ефекти скасування за допомогою float.

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


19
  • Подвійний - 64, а одна точність (поплавок) - 32 біта.
  • У подвійного є більша мантіса (цілі біти від дійсного числа).
  • Будь-які неточності будуть вдвічі меншими.

12

Розмір чисел, що беруть участь у обчисленнях з плаваючою комою, - не найрелевантніша річ. Це відповідний розрахунок.

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

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


9

Поплавок типу, довжиною 32 біти, має точність 7 цифр. Хоча він може зберігати значення з дуже великим або дуже малим діапазоном (+/- 3,4 * 10 ^ 38 або * 10 ^ -38), він має лише 7 значущих цифр.

Тип подвійний, 64 біт завдовжки, має більший діапазон (* 10 ^ + / - 308) і точність 15 цифр.

Тип "довгий подвійний" номінально становить 80 біт, хоча даний компілятор / з'єднання з ОС може зберігати його як 12-16 байт для цілей вирівнювання. Довгий дубль має показник, який просто смішно величезний і повинен мати точність 19 цифр. Microsoft, у своїй безмежній мудрості, обмежує довгий подвійний 8 байт, те саме, що звичайний подвійний.

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



9

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

#include <iostream>
#include <iomanip>

int main(){
  for(float t=0;t<1;t+=0.01){
     std::cout << std::fixed << std::setprecision(6) << t << std::endl;
  }
}

Вихід є

0.000000
0.010000
0.020000
0.030000
0.040000
0.050000
0.060000
0.070000
0.080000
0.090000
0.100000
0.110000
0.120000
0.130000
0.140000
0.150000
0.160000
0.170000
0.180000
0.190000
0.200000
0.210000
0.220000
0.230000
0.240000
0.250000
0.260000
0.270000
0.280000
0.290000
0.300000
0.310000
0.320000
0.330000
0.340000
0.350000
0.360000
0.370000
0.380000
0.390000
0.400000
0.410000
0.420000
0.430000
0.440000
0.450000
0.460000
0.470000
0.480000
0.490000
0.500000
0.510000
0.520000
0.530000
0.540000
0.550000
0.560000
0.570000
0.580000
0.590000
0.600000
0.610000
0.620000
0.630000
0.640000
0.650000
0.660000
0.670000
0.680000
0.690000
0.700000
0.710000
0.720000
0.730000
0.740000
0.750000
0.760000
0.770000
0.780000
0.790000
0.800000
0.810000
0.820000
0.830000
0.839999
0.849999
0.859999
0.869999
0.879999
0.889999
0.899999
0.909999
0.919999
0.929999
0.939999
0.949999
0.959999
0.969999
0.979999
0.989999
0.999999

Як ви бачите після 0,83, точність значно знижується.

Однак, якщо я налаштувався t як подвійний, такої проблеми не буде.

Щоб зрозуміти цю незначну помилку, яка зіпсувала мою програму, мені знадобилося п’ять годин.


4
просто щоб бути впевненим: рішенням вашого питання має бути використання int, бажано? Якщо ви хочете повторити 100 разів, вам слід порахувати з int, а не використовувати подвійний
BlueTrin

8
Використання doubleтут не є хорошим рішенням. Ви використовуєте intдля підрахунку та внутрішнього множення, щоб отримати значення з плаваючою комою.
Річард


3

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


3

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


1

Якщо хтось працює з вбудованою обробкою, з часом базове обладнання (наприклад, FPGA або якась конкретна модель процесора / мікроконтролера) буде плавно реалізовано в апаратному забезпеченні, тоді як подвійний буде використовувати програмні програми. Отже, якщо точності поплавця достатньо для задоволення потреб, програма виконає кілька разів швидше з поплавцем, а потім удвічі. Як зазначалося в інших відповідях, остерігайтеся помилок накопичення.


-1

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


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