Я обнаружил, что если вы скажете, чтобы он автоматически проверял память после завершения программы, он позволяет учитывать все статические объекты. Я использовал log4cxx и boost, которые делают много распределений в статических блоках, это фиксировало мои «ложные срабатывания» ...
Добавьте следующую строку вместо вызова _CrtDumpMemoryLeaks где-то в начале основного ( ):
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
Дополнительные сведения об использовании и макросах см. в статье MSDN:
http://msdn.microsoft.com/en-us/library /5at7yxcs(v=vs.71).aspx
Огромная разница.
Как следует из названия, a double
имеет в 2 раза большую точность, чем float
[1] sup>. В общем случае a double
имеет 15 десятичных цифр точности, в то время как float
имеет 7.
Вот как рассчитывается количество цифр:
double
имеет 52 бита мантиссы + 1 скрытый бит: log (2 53 sup>) ÷ log (10) = 15,95 цифры
float
имеет 23 бита мантиссы + 1 скрытый бит: log (2 24 sup>) ÷ 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
, а double - около 1.7e308
, поэтому использование float
может поразить «бесконечность» (т.е. специальное число с плавающей точкой) гораздо проще, чем double
для чего-то простого, например вычисление факториала 60.
Во время тестирования, возможно, несколько тестовых примеров содержат эти огромные числа, которые могут привести к сбою ваших программ, если вы используете float.
Конечно, иногда даже double
недостаточно точен, поэтому у нас иногда есть long double
[1] sup> (в приведенном выше примере на Mac 9.0000000000000066), но все Типы с плавающей запятой страдают от ошибок округления , поэтому, если точность очень важна (например, обработка денег), вы должны использовать int
или класс дроби.
Кроме того, не используйте +=
для суммирования большого числа чисел с плавающей запятой, так как ошибки быстро накапливаются. Если вы используете Python, используйте fsum
. В противном случае попробуйте реализовать алгоритм суммирования Кахана .
[1]: стандарты C и C ++ не определяют представление float
, double
и long double
. Вполне возможно, что все три реализованы как IEEE двойной точности. Тем не менее, для большинства архитектур (gcc, MSVC; x86, x64, ARM) float
действительно действительно число с плавающей запятой IEEE с одинарной точностью (binary32), а double
равно число с плавающей точкой двойной точности IEEE (binary64). sup>
Вот что говорится в стандартах 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 являются точными с наибольшей точностью.
Дано квадратное уравнение: x 2 sup> & minus; 4.0000000 & nbsp; x & nbsp; + 3.9999999 = nbsp; = & nbsp; 0, точные корни из 10 значащих цифр: r 1 & nbsp; = & nbsp; 2.000316228 и r 2 & nbsp; = & nbsp ; 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
.
(На самом деле вышеизложенное не является лучшим способом решения квадратных уравнений с использованием чисел с плавающей запятой одинарной или двойной точности, но ответ остается неизменным, даже если используется более стабильный метод .)
Размер чисел, участвующих в вычислениях с плавающей запятой, не является самым важным. Это расчет, который выполняется, который имеет отношение к делу.
По сути, если вы выполняете вычисление, а результатом является иррациональное число или повторяющееся десятичное число, то при округлении этого числа в используемую вами структуру данных конечного размера будут возникать ошибки округления. Поскольку 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
как double, такой проблемы не будет.
Мне потребовалось пять часов, чтобы понять эту незначительную ошибку, которая разрушила мою программу.
При использовании чисел с плавающей запятой вы не можете полагать, что ваши локальные тесты будут точно такими же, как тесты, выполняемые на стороне сервера. Среда и компилятор, вероятно, различаются в вашей локальной системе и в том месте, где выполняются финальные тесты. Я видел эту проблему много раз в некоторых соревнованиях TopCoder, особенно если вы пытаетесь сравнить два числа с плавающей запятой.
Встроенные операции сравнения отличаются тем, что при сравнении двух чисел с плавающей запятой разница в типе данных (то есть с плавающей запятой или двойной) может привести к разным результатам.
В отличие от int
(целое число), a float
имеют десятичную точку, как и double
. Но разница между ними заключается в том, что a double
вдвое более детально, чем float
, что означает, что он может иметь двойное количество чисел после десятичной запятой.
Тип float, длина 32 бита, точность 7 цифр. Хотя он может хранить значения с очень большим или очень маленьким диапазоном (+/- 3,4 * 10 ^ 38 или * 10 ^ -38), он имеет только 7 значащих цифр.
Тип double, длиной 64 бита, имеет больший диапазон (* 10 ^ + / - 308) и точность до 15 цифр.
Тип long double номинально равен 80 битам, хотя для данной пары компилятор / ОС может сохранять его как 12-16 байт для целей выравнивания. Длинный дубль имеет показатель, который просто смехотворно огромен и должен иметь точность до 19 цифр. Microsoft, в своей бесконечной мудрости, ограничивает long double до 8 байтов, так же, как обычный double.
Вообще говоря, просто используйте тип double, когда вам нужно значение / переменная с плавающей запятой. Литеральные значения с плавающей запятой, используемые в выражениях, будут по умолчанию рассматриваться как двойные, а большинство математических функций, возвращающих значения с плавающей запятой, возвращают двойные. Если вы просто используете double, вы избавите себя от многих головных болей и переадресации.