в чем разница между float и double?

<p>я читал о разнице между двойной точностью и одинарной точностью. однако в большинстве случаев <code>float</code> и <code>double</code> кажутся взаимозаменяемыми, т. е. использование одного или другого, похоже, не влияет на результаты. это действительно так? когда поплавки и двойники взаимозаменяемы? каковы различия между ними?</p>

<p>огромная разница.</p><p>как видно из названия, <a href="http://en.wikipedia.org/wiki/double\_precision\_floating-point\_format"><code>double</code></a> имеет в 2 раза большую точность, чем <a href="http://en.wikipedia.org/wiki/single\_precision\_floating-point\_format"><code>float</code></a> <sup>[1]</sup>, в общем случае <code>double</code> имеет 15 десятичных цифр точности, в то время как <code>float</code> имеет 7.</p><p>вот как рассчитывается количество цифр:</p><blockquote> <p><code>double</code> имеет 52 бита мантиссы + 1 скрытый бит: log (2<sup>53</sup>) ÷ log (10) = 15,95 цифр</p> <p><code>float</code> имеет 23 бита мантиссы + 1 скрытый бит: log (2<sup>24</sup>) ÷ log (10) = 7,22 цифры</p> </blockquote><p>эта потеря точности может привести к ошибкам усечения, которые легче всплыть, например,.</p><pre><code>float a = 1.f / 81; float b = 0; for (int i = 0; i < 729; ++ i) b += a; printf("%.7g\n", b); // prints 9.000023 </code></pre><p>в то время как</p><pre><code>double a = 1.0 / 81; double b = 0; for (int i = 0; i < 729; ++ i) b += a; printf("%.15g\n", b); // prints 8.99999999999996 </code></pre><p>кроме того, максимальное значение с плавающей точкой составляет около <code>3e38</code>, а double - около <code>1.7e308</code>, поэтому использование <code>float</code> может привести к «бесконечности» (то есть специальному числу с плавающей запятой) гораздо проще, чем <code>double</code> для чего-то простого, например, вычисление факториала 60.</p><p>во время тестирования, возможно, несколько тестовых примеров содержат эти огромные числа, которые могут вызвать сбой ваших программ, если вы используете float.</p><hr><p>конечно, иногда даже <code>double</code> недостаточно точен, поэтому у нас иногда есть <code>long double</code><sup>[1]</sup> (приведенный выше пример дает 9.000000000000000066 на mac), но все типы с плавающей запятой страдают от ошибок <em>округления</em>, поэтому, если точность очень важна (например, обработка денег), вы должны использовать <code>int</code> или класс дроби.</p><hr><p>кроме того, не используйте <code>+=</code> для суммирования большого числа чисел с плавающей запятой, так как ошибки быстро накапливаются. если вы используете python, используйте <code>fsum</code>. в противном случае попробуйте реализовать <a href="http://en.wikipedia.org/wiki/kahan\_summation\_algorithm">алгоритм суммирования кахана</a> .</p><hr><p><sup>[1]: стандарты c и c++ не определяют представление <code>float</code>, <code>double</code> и <code>long double</code>. вполне возможно, что все три реализованы как ieee двойной точности. тем не менее, для большинства архитектур (gcc, msvc; x86, x64, arm) <code>float</code> <em>является</em> действительно числом с плавающей запятой одинарной точности ieee (binary32), а <code>double</code> <em>является</em> числом с плавающей запятой двойной точности ieee (binary64).</sup></p>

<p>вот что говорят стандартные стандарты c99 (iso-iec 9899 6.2.5 §10) или c++ 2003 (iso-iec 14882-2003 3.1.9 §8):</p><blockquote> <p>существует три типа с плавающей запятой: <code>float</code>, <code>double</code> и <code>long double</code>. тип <code>double</code> обеспечивает, по крайней мере, такую же точность, что и <code>float</code>, а тип <code>long double</code> обеспечивает, по крайней мере, такую же точность, как и <code>double</code>. набор значений типа <code>float</code> является подмножеством набора значений типа <code>double</code>; набор значений типа <code>double</code> является подмножеством набора значений типа <code>long double</code>.</p> </blockquote><p>стандарт c++ добавляет:</p><blockquote> <p>представление значений типов с плавающей запятой определяется реализацией.</p> </blockquote><p>я бы посоветовал взглянуть на превосходное <a href="http://docs.sun.com/source/806-3568/ncg\_goldberg.html">что должен знать каждый ученый-компьютерщик об арифметике с плавающей запятой</a> которое подробно описывает стандарт ieee с плавающей запятой. вы узнаете о деталях представления и поймете, что есть компромисс между величиной и точностью. точность представления с плавающей запятой увеличивается с уменьшением величины, поэтому числа с плавающей запятой от -1 до 1 - это числа с наибольшей точностью.</p>

<p>дано квадратное уравнение: <em>x</em><sup>2</sup>- 4.0000000 <em>x</em> + 3.9999999 = 0, точные корни до 10 значащих цифр, <em>r</em><sub>1</sub>= 2.000316228 и <em>r</em><sub>2</sub>= 1,999683772.</p><p>используя <code>float</code> и <code>double</code>, мы можем написать тестовую программу:</p><pre><code>#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; } </code></pre><p>запуск программы дает мне:</p><pre><code>2.00000 2.00000 2.00032 1.99968 </code></pre><p>обратите внимание, что числа не велики, но вы все равно получаете эффекты отмены, используя <code>float</code>.</p><p>(на самом деле, вышеупомянутое не является лучшим способом решения квадратных уравнений с использованием чисел с плавающей запятой одинарной или двойной точности, но ответ остается неизменным, даже если кто-то использует <a href="http://en.wikipedia.org/wiki/quadratic\_equation#floating\_point\_implementation">более стабильный метод</a> .)</p>

<ul> <li>двойное число равно 64, а одинарная точность (float) составляет 32 бита.</li> <li>двойник имеет большую мантиссу (целые биты действительного числа).</li> <li>любые неточности будут в два раза меньше.</li> </ul>

<p>размер чисел, участвующих в вычислениях с плавающей точкой, - не самая важная вещь. это расчет, который выполняется, который имеет отношение к делу. </p><p>по сути, если вы выполняете вычисление, а результатом является иррациональное число или повторяющееся десятичное число, то при округлении этого числа в используемую вами структуру данных конечного размера будут возникать ошибки округления. поскольку double в два раза больше числа с плавающей точкой, ошибка округления будет намного меньше.</p><p>тесты могут специально использовать числа, которые могут вызвать такую ошибку, и поэтому проверяли, что вы использовали соответствующий тип в своем коде.</p>

<p>тип float, 32 бита, имеет точность 7 цифр. хотя он может хранить значения с очень большим или очень маленьким диапазоном (+/- 3,4 * 10 ^ 38 или * 10 ^ -38), он имеет только 7 значащих цифр.</p><p>тип double, длиной 64 бита, имеет больший диапазон (* 10 ^ +/- 308) и точность до 15 цифр.</p><p>тип long double номинально равен 80 битам, хотя для данной пары компилятор/ос может сохранять его как 12-16 байтов для целей выравнивания. длинный дубль имеет показатель, который просто смехотворно огромен и должен иметь точность до 19 цифр. microsoft, в своей бесконечной мудрости, ограничивает long double до 8 байтов, так же, как обычный double.</p><p>вообще говоря, просто используйте тип double, когда вам нужно значение/переменную с плавающей запятой. литеральные значения с плавающей запятой, используемые в выражениях, будут по умолчанию рассматриваться как двойные, и большинство математических функций, возвращающих значения с плавающей запятой, возвращают двойные. вы избавите себя от многих головных болей и типов, если вы просто используете double.</p>

<p>поплавки имеют меньшую точность, чем двойные. хотя вы уже знаете, прочитайте <a href="http://docs.sun.com/source/806-3568/ncg\_goldberg.html">что we следует знать об арифметике с плавающей точкой</a> для лучшего понимания.</p>

<p>я только что натолкнулся на ошибку, которая потребовала у меня целую вечность, чтобы выяснить это и потенциально может дать вам хороший пример точности с плавающей точкой.</p><pre><code>#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; } } </code></pre><p>результат</p><pre><code>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 </code></pre><p>как вы можете видеть после 0,83, точность значительно снижается.</p><p>однако, если я установлю <code>t</code> как double, такой проблемы не будет.</p><p>мне потребовалось пять часов, чтобы понять эту незначительную ошибку, которая разрушила мою программу.</p>

<p>при использовании чисел с плавающей запятой вы не можете полагать, что ваши локальные тесты будут точно такими же, как тесты, выполняемые на стороне сервера. среда и компилятор, вероятно, различаются в вашей локальной системе и в том месте, где выполняются финальные тесты. я видел эту проблему много раз в некоторых соревнованиях topcoder, особенно если вы пытаетесь сравнить два числа с плавающей запятой.</p>

<p>встроенные операции сравнения отличаются тем, что при сравнении двух чисел с плавающей запятой разница в типе данных (то есть с плавающей запятой или двойной) может привести к разным результатам.</p>

<p>разница между float и double заключается в том, что double имеет большее значение точности, чем переменные с плавающей точкой . когда вы объявляете переменную как float, она позволяет вводить только 6 десятичных разрядов после десятичной точки ..\_\_ переменная с плавающей точкой <code>float f= 2.3333333;</code> // 7 знаков после запятой - это максимум, который вы можете сохранить</p><p>даже если вы сохраните значение, которое больше десяти цифр после десятичной запятой, оно не сохранит все число, а сохранит только первые шесть цифр после десятичной запятой .... также вы получите ошибку если вы попытаетесь сохранить более 7 цифр после десятичной точки для этой переменной. в этом случае вам нужно инициализировать его следующим образом - <code>float f= 2.3333334443f;</code> // тогда компилятор интерпретирует это как 2.3333334</p><p>в случае double, он будет хранить до 15 цифр после десятичной точки . пример: <code>double d=1.222222345675423;</code> // 15 цифр после десятичной точки</p>