Чем double отличается от float в c
Перейти к содержимому

Чем double отличается от float в c

  • автор:

Вещественные типы (double, float)

Вещественные типы (или типы с плавающей точкой) представляют значения, имеющие дробную часть. В языке MQL4 есть два типа для чисел с плавающей точкой. Способ представления вещественных чисел в машинной памяти определен стандартом IEEE 754 и не зависит от платформ, операционных систем и языков программирования.

Размер в байтах

Минимальное положительное значение

Имя double означает, что точность этих чисел вдвое превышает точность чисел типа float . В большинстве случаев тип double является наиболее удобным. Ограниченной точности чисел float во многих случаях попросту недостаточно. Причина, по которой тип float все еще используется, — экономия памяти при хранении (это важно для больших массивов вещественных чисел).

Константы с плавающей точкой состоят из целой части, точки (.) и дробной части. Целая и дробная части представляют собой последовательности десятичных цифр.

double a= 12.111 ;
double b=- 956.1007 ;
float c = 0.0001 ;
float d = 16 ;

Существует научный способ записи вещественных констант, зачастую этот способ записи более компактный, чем традиционный.

double c1=1.12123515e-25;
double c2=0.000000000000000000000000112123515; // 24 нуля после десятичной точки

Print ( «1. c1 = » , DoubleToString (c1,16));
// Результат: 1. c1 = 0.0000000000000000

Print ( «2. c1 = » , DoubleToString (c1,-16));
// Результат: 2. c1 = 1.1212351499999999e-025

Print ( «3. c2 = » , DoubleToString (c2,-16));
// Результат: 3. c2 = 1.1212351499999999e-025

Необходимо помнить, что вещественные числа хранятся в памяти компьютера с некоторой ограниченной точностью в двоичной системе счисления, в то время как общепринятой в использовании является десятичная система счисления. Поэтому многие числа, которые точно записываются в десятичной системе, в двоичной системе можно записать только в виде бесконечной дроби.

Например, числа 0.3 и 0.7 представлены в компьютере бесконечными дробями, в то время как число 0.25 хранится точно, так как представляет из себя степень двойки.

В связи с этим, категорически не рекомендуется сравнивать между собой два вещественных числа на равенство, так как такое сравнение не является корректным.

void OnStart ()
<
//—
double three=3.0;
double x,y,z;
x=1/three;
y=4/three;
z=5/three;
if (x+y==z) Print ( «1/3 + 4/3 == 5/3» );
else Print ( «1/3 + 4/3 != 5/3» );
// Результат: 1/3 + 4/3 != 5/3
>

Если все же необходимо сравнить на равенство два вещественных числа, то можно сделать это двумя разными способами. Первый способ заключается в сравнении разницы между двумя числами с какой-то малой величиной, задающей точность сравнения.

bool EqualDoubles( double d1, double d2, double epsilon)
<
if (epsilon <0) epsilon=-epsilon;
//—
if (d1-d2>epsilon) return false;
if (d1-d2 <-epsilon) return false;
//—
return true ;
>
void OnStart ()
<
double d_val=0.7;
float f_val=0.7;
if (EqualDoubles(d_val,f_val,0.000000000000001)) Print (d_val, «equals» ,f_val);
else Print ( «Different: d_val = » , DoubleToString (d_val,16),
» f_val = » , DoubleToString (f_val,16));
// Результат: Different: d_val= 0.7000000000000000 f_val= 0.6999999880790710
>

Необходимо отметить, что значение параметра epsilon в приведенном примере не может быть меньше предопределенной константы DBL_EPSILON. Значение этой константы 2.2204460492503131e-016. Для типа float соответствующая константа FLT_EPSILON = 1.192092896e-07. Смысл этих значений таков, что это наименьшее значение, удовлетворяющее условию 1.0+DBL_EPSILON != 1.0 (для чисел типа float 1.0+FLT_EPSILON != 1.0).

Второй способ предполагает сравнивать нормализованную разность двух вещественных чисел с нулевым значением. Сравнивать разность нормализованных чисел с нулём бесполезно, так как в результате любой математической операции с нормализованными числами результат получается ненормализованным.

bool CompareDoubles( double number1, double number2)
<
if ( NormalizeDouble (number1-number2,8)==0) return ( true );
else return ( false );
>
void OnStart ()
<
double d_val=0.3;
float f_val=0.3;
if (CompareDoubles(d_val,f_val)) Print (d_val, «equals» ,f_val);
else Print ( «Different: d_val = » , DoubleToString (d_val,16),
» f_val = » , DoubleToString (f_val,16));
// Результат: Different: d_val= 0.3000000000000000 f_val= 0.3000000119209290
>

В результате некоторых операций математического сопроцессора может получиться недействительное вещественное число, которое нельзя использовать в математических операциях и операциях сравнения, так как результат выполнения операций над недействительными вещественными числами неопределен. Например, при попытке вычислить арксинус от 2, результатом будет минус бесконечность.

double abnormal = MathArcsin (2.0);
Print ( «MathArcsin(2.0) =» ,abnormal);
// Результат: MathArcsin(2.0) = -1.#IND

Кроме минус бесконечности существуют плюс бесконечность и NaN (не число). Чтобы определить, что данное число недействительно, можно использовать функцию MathIsValidNumber(). По стандарту IEEE они имеют специальное машинное представление. Например, плюс бесконечность для типа double имеет битовое представление 0x7FF0 0000 0000 0000.

struct str1
<
double d;
>;
struct str2
<
long l;
>;

//— начнем
str1 s1;
str2 s2;
//—
s1.d= MathArcsin (2.0); // получим недействительное число -1.#IND
s2=s1;
printf ( «1. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0xFFFF000000000000; // недействительное число -1.#QNAN
s1=s2;
printf ( «2. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x7FF7000000000000; // наиобльшее нечисло SNaN
s1=s2;
printf ( «3. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x7FF8000000000000; // наименьшее нечисло QNaN
s1=s2;
printf ( «4. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x7FFF000000000000; // наибольшее нечисло QNaN
s1=s2;
printf ( «5. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x7FF0000000000000; // плюс бесконечность 1.#INF и наименьшее нечисло SNaN
s1=s2;
printf ( «6. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0xFFF0000000000000; // минус бесконечность -1.#INF
s1=s2;
printf ( «7. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x8000000000000000; // отрицательный ноль -0.0
s1=s2;
printf ( «8. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x3FE0000000000000; // 0.5
s1=s2;
printf ( «9. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x3FF0000000000000; // 1.0
s1=s2;
printf ( «10. %f %I64X» ,s1.d,s2.l);
//—
s2.l=0x7FEFFFFFFFFFFFFF; // наибольшее нормализованное число (MAX_DBL)
s1=s2;
printf ( «11. %.16e %I64X» ,s1.d,s2.l);
//—
s2.l=0x0010000000000000; // наименьшее положительное нормализованное (MIN_DBL)
s1=s2;
printf ( «12. %.16e %.16I64X» ,s1.d,s2.l);
//—
s1.d=0.7; // покажем, что число 0.7 – бесконечная дробь
s2=s1;
printf ( «13. %.16e %.16I64X» ,s1.d,s2.l);
/*
1. -1.#IND00 FFF8000000000000
2. -1.#QNAN0 FFFF000000000000
3. 1.#SNAN0 7FF7000000000000
4. 1.#QNAN0 7FF8000000000000
5. 1.#QNAN0 7FFF000000000000
6. 1.#INF00 7FF0000000000000
7. -1.#INF00 FFF0000000000000
8. -0.000000 8000000000000000
9. 0.500000 3FE0000000000000
10. 1.000000 3FF0000000000000
11. 1.7976931348623157e+308 7FEFFFFFFFFFFFFF
12. 2.2250738585072014e-308 0010000000000000
13. 6.9999999999999996e-001 3FE6666666666666
*/

Чем double отличается от float в c

На 32-разрядной платформе ARM, в среде разработки IAR EWB for ARM применены следующие типы float, double и long double.

float — 32-битное число с плавающей точкой. Диапазон чисел от ±1.18E-38 до ±3.39E+38, 7 десятичных разрядов, 8 бит показателя степени (экспонента) и 23 бита основания степени (мантисса).

double — 64-битное число с плавающей точкой. Диапазон чисел от ±2.23E-308 to ±1.79E+308, 15 десятичных разрядов, 11 бит показателя степени (экспонента) и 52 бита основания степени (мантисса).

long double — абсолютно то же самое, что и double.

Операции с числами в формате с плавающей точкой весьма неэффективны по затратам кода, памяти и процессорного времени, поэтому таких чисел нужно по возможности избегать, или хотя бы использовать float вместо double.

Информация получена из документа справки «IAR C/C++ Development Guide» EWARM_DevelopmentGuide.ENU.pdf, раздел «FLOATING-POINT TYPES».

[См. также]

1. Описание стандартных функций < cmath >(math.h) site:cplusplus.com .
2. Как избавиться от чисел с плавающей точкой.

В чем отличие float/double и decimal?

Здравствуйте, хотел попросить какие-то материалы, которые будут понятны новичку, насчёт ошибок округления и типа decimal, заранее спасибо!

  • Вопрос задан 28 дек. 2022
  • 1318 просмотров

1 комментарий

Простой 1 комментарий

В MSDN смотрели?
https://learn.microsoft.com/ru-ru/dotnet/csharp/la.
Решения вопроса 1

vabka

Василий Банников @vabka Куратор тега C#
Токсичный шарпист

float и double — это числа с плавающей точкой по стандарту IEEE754. Операции с ними происходят достаточно быстро, тк они реализованы в процессоре на аппаратном уровне.
Но они достаточно не точные в плане выражения десятичных дробей. (То самое 0.1+0.2 != 0.3).
Настоящие деньги на них считать не следует.

decimal же напротив очень точный, но медленный.

C++: Типы данных с плавающей точкой

Целочисленные типы отлично подходят для подсчета целых чисел, но иногда нам нужно хранить очень большие числа или числа с дробной частью. Переменная типа с плавающей точкой – это переменная, которая может содержать действительное число, например 4320,0, -3,33 или 0,01226. Название плавающая точка указывает на то, что десятичная точка может плавать, то есть она может поддерживать переменное количество цифр до и после себя.

Существует три разных типа данных с плавающей точкой: float , double и long double . Как и в случае с целыми числами, C++ не определяет фактические размеры этих типов, но гарантирует минимальные размеры:

  • float – должен иметь как минимум 32-бита и гарантировать минимум 6 значащих цифр
  • double – 48-бит и быть не меньше float. Гарантирует 15 значащих цифр
  • long double – как минимум 80 битов и 18-значащих цифр

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

Ниже представлены примеры определения переменных с плавающей точкой:

float f_num; double d_num; long double ld_num; 

Вы могли заметить, что в имени переменной используется префикс — это делать не обязательно, но это помогает понять переменная кого типа сейчас используется.

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

double d_num < 5.0 >; float f_num < 5.0f >; 

По умолчанию литералы с плавающей точкой имеют тип double . Суффикс f используется для обозначения литерала типа float .

Вы могли заметить, что в коде выше инициализация производится не через оператор присваивания = , а с помощью нотации < 5.0 >. Это списковая инициализация, она пришла в язык с 11 стандарта и она имеет одно преимущество:

int num; num = 3.14; std::cout 3 

В коде выше мы пытаемся сохранить в целочисленную переменную вещественное число. Такая программа скомпилируется и будет работать, но у числа мы потеряем всю вещественную часть.

Если применить списковую инициализацию, получим ошибку компиляции:

int num < 3.14 >; // ошибка int count; count = < 3.14 >; // ошибка 

Рассмотрим такую программу:

int main() < float f_num = < 10.0 / 3.0 >; double d_num = < 10.0 / 3.0 >; std::cout
3.33333 3.33333

Обратите внимание, что каждое из напечатанных значений имеет только 6 значащих цифр. Это стандартное поведение объекта cout , но мы можем переопределить это поведение и повысить точность:

#include #include // для увеличения точности int main() < std::cout ; double d_num = < 10.0 / 3.0 >; std::cout
3.333333253860474 3.333333333333333

Видно, что число float определено не точно и содержит ошибки, поскольку тип float гарантирует нам только шесть значащих цифр.

Существует две особые категории чисел с плавающей точкой. Первая – Inf , которая представляет бесконечность. Inf может быть положительной или отрицательной. Вторая – NaN , что означает Not a Number — не число. Существует несколько различных типов NaN . NaN и Inf доступны только в том случае, если компилятор для чисел с плавающей точкой использует определенный формат. Если используется другой формат, следующий код приводит к неопределенному поведению.

#include int main() < double zero < 0.0 >; double pos_inf < 5.0 / zero >; // положительная бесконечность std::cout ; // отрицательная бесконечность std::cout ; // не число (математически неверно) std::cout
inf -inf nan

Задание

Напишите программу, которая рассчитывает сколько километров проедет автомобиль на полном баке. Пусть объем бака будет 43 литра, а расход 8.8 л на 10 км. Результат выведите на экран.

Упражнение не проходит проверку — что делать? ��

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

  • Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.

В моей среде код работает, а здесь нет ��

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя ��

Это нормально ��, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно ��

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *