В чем разница между float и double?
Я читал о разнице между двойной точностью и плавающей запятой одинарной точности. Однако, в большинстве случаев, float и double кажутся взаимозаменяемыми, т. е. использование одного или другого не влияет на результаты. Это действительно так? Когда поплавки и двойники взаимозаменяемы? В чем разница между ними?
11 ответов:
огромная разница.
как следует из названия, a
doubleимеет 2x точностьfloat[1]. В общемdoubleсостоит из 15 десятичных цифр точности, в то время какfloatв 7.вот как вычисляется количество цифр:
doubleимеет 52 бит мантиссы + 1 скрытый бит: log(253)÷log (10) = 15,95 цифр
floatв 23 мантиссы + 1 скрытый бит: log (224)÷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или класс fraction.
кроме того, не используйте
+=суммировать множество чисел с плавающей запятой, так как ошибки накапливаются быстро. Если вы используете Python, используйтеfsum. В противном случае попробуйте реализовать алгоритм суммирования Кахана.
[1]: стандарты C и C++ не определяют представление
float,doubleиlong double. Возможно, что все три реализованы как IEEE двойной точности. Тем не менее, для большинства архитектур (gcc, MSVC; x86, x64, ARM)floatи действительно число с плавающей запятой IEEE с одной точностью (binary32) иdoubleи число с плавающей запятой двойной точности IEEE (binary64).
вот что говорят стандарты стандарта 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; набор значений типаdoubleis подмножество значений типаlong double.стандарт C++ добавляет:
представление значений типов с плавающей запятой определяется реализацией.
Я бы предложил взглянуть на отличные Что Каждый Компьютерный Ученый Должен Знать Об Арифметике С Плавающей Запятой это охватывает стандарт IEEE с плавающей запятой в глубину. Вы узнаете о деталях представления, и вы поймете существует компромисс между величиной и точностью. Точность представления с плавающей запятой увеличивается по мере уменьшения величины, поэтому числа с плавающей запятой между -1 и 1 являются наиболее точными.
дано квадратное уравнение: x2 - 4.0000000 x + 3.9999999 = 0, точные корни до 10 значащих цифр,r1 = 2.000316228 и r2 = 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.(на самом деле, вышеизложенное не является лучшим способом решения квадратичных уравнений с использованием чисел с плавающей запятой с одинарной или двойной точностью, но ответ остается неизменным, даже если используется более стабильный способ.)
- двойник 64 и одиночная точность (float) - 32 бита.
- двойник имеет большую мантиссу (целочисленные биты действительного числа).
- любые неточности будут меньше в двойном.
размер чисел, участвующих в вычислениях с плавающей точкой, не является самым важным. Это расчет, который выполняется, что является актуальным.
по сути, если вы выполняете вычисление, и результатом является иррациональное число или повторяющееся десятичное число, то будут ошибки округления, когда это число будет сжато в структуру данных конечного размера, которую вы используете. Поскольку double в два раза больше размера float, то ошибка округления будет много меньший.
тесты могут специально использовать числа, которые вызвали бы такую ошибку и поэтому проверили, что вы использовали соответствующий тип в своем коде.
поплавки имеют меньшую точность, чем парный. Хотя вы уже знаете, читайте что мы должны знать об арифметике с плавающей запятой для лучшего понимания.
тип поплавок, 32 бита длиной, имеет точность 7 цифр. Хотя он может хранить значения с очень большим или очень малым диапазоном (+/- 3.4 * 10^38 или * 10^-38), он имеет только 7 значащих цифр.
тип двойной, 64 бита длиной, имеет более большой ряд (*10^+/-308) и точность 15 чисел.
тип long double номинально составляет 80 бит, хотя данное сопряжение компилятора/ОС может хранить его как 12-16 байт для целей выравнивания. У длинного двойника есть показатель, который просто смехотворно огромен и должен иметь точность 19 цифр. Microsoft, в своей бесконечной мудрости, ограничивает long double до 8 байт, так же, как и простой double.
вообще говоря, просто используйте тип double, когда вам нужно значение с плавающей запятой/переменная. Литеральные значения с плавающей запятой, используемые в выражениях, по умолчанию будут рассматриваться как двойные, а большинство математических функций, возвращающих значения с плавающей запятой, возвращают двойные значения. Вы избавите себя от многих головных болей и типизаций, если вы просто используете double.
Я просто столкнулся с ошибкой, которая заняла у меня целую вечность, чтобы выяснить, и потенциально может дать вам хороший пример точности поплавка.
#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как двойной, такой вопрос, не произойдет.мне потребовалось пять часов, чтобы понять эту незначительную ошибку, которая разрушила мою программу.
при использовании чисел с плавающей запятой вы не можете доверять, что ваши локальные тесты будут точно такими же, как тесты, выполняемые на стороне сервера. Среда и компилятор, вероятно, отличаются от вашей локальной системы и где выполняются окончательные тесты. Я видел эту проблему много раз раньше в некоторых соревнованиях TopCoder, особенно если вы пытаетесь сравнить два числа с плавающей запятой.
встроенные операции сравнения отличаются, как и при сравнении 2 чисел с плавающей точкой, разница в типе данных (т. е. float или double) может привести к различным результатам.
в отличие от
int(целое число), afloatесть десятичная точка, и так можетdouble. Но разница между ними заключается в том, что adoubleв два раза более подробно, чем afloat, что означает, что он может иметь двойное количество чисел после десятичной точки.
Comments