Как найти значение машинного нуля

Определяем машинный ноль, машинную бесконечность и машинный эпсилон

Доверять расчёту, сделанному на компьютере, без тени понимания того, как именно выполнен этот расчёт — одна из худший вещей, которые может допустить в своей работе инженер. К сожалению, уже нередки «специалисты», которых не смущает ненулевой результат, полученный при умножении на ноль, или, напротив, ноль там, где теоретически нуля быть не должно.

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

В IBM-совместимой ЭВМ для вещественных чисел используется двоичная система счисления и принята форма представления чисел с плавающей точкой вида

x = m*2p, где мантисса m = ± (g1*2-1 +
g2*2-2 + ... +
gt*2-t)
,

g1, ..., gt — двоичные цифры, причём, g1=1, а целое значение p называется двоичным порядком. Количество цифр t, которое отводится для записи мантиссы, называется разрядностью мантиссы. Диапазон представления чисел в ЭВМ ограничен конечной разрядностью мантиссы и значением числа p.

Все представимые на ЭВМ вещественные числа x удовлетворяют неравенствам
0 < X0 ≤ |x| < X, где
X0 = 2-pmax+1,
X = 2pmax, а значение pmax соответствует разрядности вычислительной системы.

Все числа, по модулю большие X, не представимы на ЭВМ и рассматриваются как машинная бесконечность. Все числа, по модулю меньшие X0, для компьютера не отличаются от нуля и рассматриваются как машинный ноль. Машинным эпсилон εM называется относительная точность ЭВМ, то есть граница относительной погрешности представления вещественных чисел. Можно показать, что εM ≈ 2-t. Пусть
x* = m*2p. Тогда граница абсолютной погрешности представления этого числа равна Δ(x*) ≈ 2-t-1*2p. Поскольку 1/2≤m<1, то величина относительной погрешности представления оценивается как
δ(x*) ≈ Δ(x*) / |x*| ≈ (2-t-1*2p) / (m*2p) = 2-t-1 / m ≤ 2-t-1 / 2-1 = 2-t.

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

Примем следующие способы определения приближённых значений искомых величин:

  • положим X = 2n, где n — первое натуральное число, при котором произошло переполнение;
  • положим X0 = 2-m, где m – первое натуральное число , при котором 2-m совпадает с нулем;
  • положим εM = 2-k, где k – наибольшее натуральное число, при котором сумма вычисленного значения 1+2-k ещё больше 1. Фактически, εM есть граница относительной погрешности представления числа x* ≈ 1.

Дальше задаём это в нужной среде (пакете) и подбираем значения параметров, вот пример для моего MathCAD 15:

машинный ноль, машинная бесконечность и машинный эпсилон в MathCAD 15

машинный ноль, машинная бесконечность и машинный эпсилон в MathCAD 15

А вот что вышло в Visual Studio 2010 при использовании проекта Windows Forms, C++/CLI, библиотеки System::Math и типа данных long double:

Inf: 1024
Zero: 1075
Eps: 53

Код:

//Функции для подсчёта
long double inf (int n) { return Math::Pow(2.,n); }
long double zero (int m) { return Math::Pow(2.,-m); }
long double eps (int k) { return 1.+Math::Pow(2.,-k); }
//...
//Расчёт, сделанный по нажатию кнопки с выводом результатов в метку label1
 label1->Text = "";

 int n=1,m=1,k=1;
 long double res;
 while (1) {
  res=inf(n);
  if (res==Double::PositiveInfinity) break;
  else n++;
 };
 label1->Text +=  "Inf: " + n + Environment::NewLine;

 while (1) {
  res=zero(m);
  if (res==0.) break;
  else m++;
 };
 label1->Text +=  "Zero: " + m + Environment::NewLine;

 while (1) {
  res=eps(k);
  if (res==1.) break;
  else k++;
 };
 label1->Text +=  "Eps: " + k + Environment::NewLine;

Ну и пара стандартных напоминаний напоследок:

К вещественным значениям в общем случае неприменима операция == («сравнение») из-за неточного представления этих значений в памяти компьютера. Поэтому для вещественных переменных отношение вида a==b обычно заменяется на fabs(a-b)≤eps, где fabs() — функция вычисления модуля вещественного числа, а eps — малая величина, определяющая допустимую погрешность.

Допустимую погрешность можно ввести в расчёт также через стандартный метод округления round, например, левый расчёт произведения чисел в MathCAD не даст нуля, а правый — да:

учёт погрешностей через метод round (Mathcad)

учёт погрешностей через метод round (Mathcad)

27.10.2015, 17:32 [26328 просмотров]


Машинный нуль (англ. computer zero) — числовое значение, меньше которого невозможно задавать точность для любого алгоритма, возвращающего вещественные числа. Абсолютное значение «машинного нуля» зависит от разрядности сетки применяемой ЭВМ, от принятой в конкретном трансляторе точности представления вещественных чисел и от значений, используемых для оценки точности.

В языках программирования

Язык Си

В языке Си существуют предельные константы FLT_EPSILON и DBL_EPSILON называемые «машинными нулями» относительно вещественного значения 1.0. FLT_EPSILON — максимальное значение типа float и имеет значение 1E-5, DBL_EPSILON — максимальное значение типа double и имеет значение 1E-16. Сумма каждого из этих значений со значение 1.0 не отличается от 1.0.

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

При представлении чисел со знаком в обратных двоичных кодах существуют проблема[уточнить] наличия двух обратных кодов числа 0: «положительный нуль» и «отрицательный нуль».

Пример

Пример вычисления машинного эпсилона (не путать с машинным нулём) на языке Си.

#include <stdio.h>
int main()
{
   float e,e1; /* e1 - вспомогательная переменная */
   int k=0; /* k - счетчик итераций */
   e=1.0;
   do
   {
      e=e/2.0;
      e1=e+1.0;
      k++;
   } while (e1>1.0);
   printf("Число делений на 2: %dn",k);
   printf("Машинный эпсилон: %en",e);
   return 0;
}

Пример на языке C++.

#include <iostream>
#include <stdint.h>
#include <iomanip>
 
template<typename float_t, typename int_t>
float_t machine_eps()
{
        union
        {
                float_t f;
                int_t   i;
        } one, one_plus, little, last_little;
 
        one.f    = 1.0;
        little.f = 1.0;
        last_little.f = little.f;
 
        while(true)
        {
                one_plus.f = one.f;
                one_plus.f += little.f;
 
                if( one.i != one_plus.i )
                {
                        last_little.f = little.f;
                        little.f /= 2.0;
                }
                else
                {
                        return last_little.f;
                }
        }
}
 
int main()
{
        std::cout << "machine epsilon:n";
        std::cout << "float: " << std::setprecision(18)<< machine_eps<float, uint32_t>() << std::endl;
        std::cout << "double: " << std::setprecision(18) << machine_eps<double, uint64_t>() << std::endl;
}

Пример на Python

def machineEpsilon(func=float):
    machine_epsilon = func(1)
    while func(1)+func(machine_epsilon) != func(1):
        machine_epsilon_last = machine_epsilon
        machine_epsilon = func(machine_epsilon) / func(2)
    return machine_epsilon_last

Вывод может быть таким (с использованием IPython):

In [1]: machineEpsilon(int)
Out[1]: 1
In [2]: machineEpsilon(float)
Out[2]: 2.2204460492503131e-16
In [3]: machineEpsilon(complex)
Out[3]: (2.2204460492503131e-16+0j)

См. также

  • −0 (программирование)

Введем в коэффициент b погрешность 0.1:

k2 := korni_uravnenia(1,-39.6 + Db1,-716.85)

æ13.5020272697128

ö

k2 = ç

53.0920272697128

÷

è

ø

Практические вычисленные погрешности: Теоретически вычисленные погрешности:

D(k1,k21) = 2.02726971282985 ´ 10− 3

f_b_x1(1, 716.85,-39.6)

×Db1 = 2.02702702702703 ´ 10− 3

D(k2,k22) = 7.97273028717171 ´ 10− 3

f_b_x2(1, 716.85,-39.6)

×Db1 = 7.97297297297297 ´ 10− 3

d(k2

1

, D(k , k2

)) = 1.50145579795808 ´ 10− 4

f_b_x1(1, 716.85,-39.6)

×Db1

= 1.50127605768799 ´ 10− 4

1

1

k21

d(k2

2

, D(k , k2

)) = 1.50168126876554 ´ 10− 4

f_b_x2(1, 716.85,-39.6)

×Db1

= 1.50172697916948 ´ 10− 4

2

2

k22

Введем в коэффициент b погрешность 0.2:

k2 := korni_uravnenia(1,-39.6 + Db2,-716.85)

æ13.5040550248839

ö

k2 = ç

÷

è 53.0840550248839

ø

Практические вычисленные погрешности: Теоретически вычисленные погрешности:

D(k1,k21) = 4.05502488394305 ´ 10− 3

f_b_x1(1, 716.85,-39.6)

×Db2 = 4.05405405405406 ´ 10− 3

D(k2,k22) = 0.015944975116064

f_b_x2(1, 716.85,-39.6)

×Db2 = 0.015945945945946

d(k2

1

, D(k , k2

)) = 3.00282017251177 ´ 10− 4

f_b_x1(1, 716.85,-39.6)

×Db2

= 3.00210125520345 ´ 10− 4

1

1

k21

d(k2

, D(k , k2

)) = 3.00372213625903 ´ 10− 4

f_b_x2(1, 716.85,-39.6)

×Db2

− 4

2

2

2

= 3.00390502166254

´ 10

k22

Введем в коэффициент b погрешность 0.3:

k2 := korni_uravnenia(1,-39.6 + Db3,-716.85)

k2 = æ13.5060832656434 ö

è 53.0760832656434 ø

Практические вычисленные погрешности: Теоретически вычисленные погрешности:

D(k1

,k21) = 6.08326564336892 ´ 103

f_b_x1(1, 716.85,-39.6)

×Db3 = 6.08108108108108 ´ 103

D(k2

,k22) = 0.023916734356632

f_b_x2(1, 716.85,-39.6)

×Db3 = 0.023918918918919

d(k21

, D(k1, k21)) = 4.50409309917662 ´ 104

f_b_x1(1, 716.85,-39.6)

×Db

3

= 4.50247563373911 ´ 104

k21

d(k22

, D(k2, k22)) = 4.50612269879261 ´ 104

f_b_x2(1, 716.85,-39.6)

×Db3

= 4.50653428950396 ´ 104

k22

Введем в коэффициент b погрешность 0.4:

k2 := korni_uravnenia(1,-39.6 + Db4,-716.85)

æ13.5081119921212

ö

k2 = ç

÷

è 53.0681119921212

ø

Практические вычисленные погрешности: Теоретически вычисленные погрешности:

D(k1

,k21) = 8.11199212114744 ´ 103

f_b_x1(1, 716.85,-39.6)

×Db4 = 8.10810810810811 ´ 103

D(k2

,k22) = 0.031888007878855

f_b_x2(1, 716.85,-39.6)

×Db4 = 0.031891891891892

d(k21

, D(k1, k21)) = 6.0052745534527 ´ 104

f_b_x1(1, 716.85,-39.6)

×Db4

= 6.00239923450243 ´ 104

k21

d(k22

, D(k2, k22)) = 6.00888305270584 ´ 104

f_b_x2(1, 716.85,-39.6)

×Db4

= 6.0096149447764 ´ 104

k22

Вывод: Влияние погрешности, внесенной в сложное математическое выражение, может быть учтено теоретически. Для определения абсолютной погрешности в данном вычислительном эксперименте применялась формула Лагранжа.

Задача 1.6. Для пакета MATHCAD найти значения машинного нуля, машинной бесконечности,

машинного эпсилон

Теоретический материал.

В ЭВМ для вещественных чисел используется двоичная система счисления и принята форма представления чисел с плавающей точкой x = μ ×2p ,

μ = ±(γ1 × 21 + γ 2 × 22 + + γ t × 2t ) . Здесь μ мантисса ; γ1,γ 2,..γt двоичные цифры, причем всегда γ1=1, p-целое число называемое двоичным порядком.

Количество t цифр, которое отводится для записи мантиссы, называется разрядностью мантиссы. Диапазон представления чисел в ЭВМ ограничен конечной разрядностью мантиссы и значением числа p. Все представимые числа на ЭВМ

удовлетворяют неравенствам: 0 < X0

x

< X, где X

0 = 2

( pmax +1)

,

X= 2pmax . Все числа, по модулю большие X, не представимы на ЭВМ и

рассматриваются как машинная бесконечность. Все числа,

по модулю меньшие X0 ,

для ЭВМ не отличаются от нуля и рассматриваются как машинный нуль.

Машинным эпсилон εM называется относительная точность ЭВМ, то есть граница относительной погрешности представления чисел в ЭВМ. Покажем, что

εM » 2t . Пусть x* = μ × 2p , тогда граница абсолютной погрешности представления этого числа равна D(x*) » 2t1 × 2p . Поскольку 12 £ μ <1, то величина относительной погрешности представления оценивается так:

(x*) »

(x*)

2t1 × 2p

2t1

£

2t1

D

»

=

= 2t .

δ

x*

μ × 2p

μ

21

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

Примем следующие способы определения приближенных значений параметров, требуемых в задаче:

1.Положим X= 2n , где n первое натуральное число, при котором происходит переполнение.

2.Положим X0 = 2m , где m первое натуральное число , при котором 2m совпадает с нулем.

Решение задачи:

МАШИННАЯ БЕСКОНЕЧНОСТЬ

inf(n) := 10n

МАШИННЫЙ НУЛЬ

zero(k) := 2k

МАШИННОЕ ЭПСИЛОН

eps(k) := 2k

Находим машинную бесконечность:

inf(307) = 1 × 10307

inf(308) =

Находим машинный нуль для MathCAD:

get_zero := a ¬ 1 k ¬ 0

while a > 0

a ¬ a2

k ¬ k 1

k

get_zero = 1.019 ´ 103

zero(get_zero) = 0

Степень двойки, при которой число представляется в

MathCAD как нуль.

Находим относительную точность в MathCAD:

get_eps1 :=

k ¬ 0

get_eps2 :=

k ¬ 0

while 1 + eps(k) > 1

while 1 eps(k) ¹ 1

k ¬ k 1

k ¬ k 1

k

k

get_eps1 = 40

get_eps2 = 40

Степень двойки, при которой число, представленное в MathCAD, участвующее в операции сложения (вычитания) c единицей, уже не вносит такого вклада в результат, который можно было бы зафиксировать.

eps(40) = 9.09494701772928 ´ 1013

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

которых сопоставимы с порядками теоретически определенных величин для современных ЭВМ вообще, что подтверждает корректную постановку эксперимента.

Задача 1.7. Вычислить значения машинного нуля, машинной бесконечности, машинного эпсилон

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

Результаты вычислительного эксперимента:

Вычисление на компиляторе C++ (Intel):

Текст программы:

#include «stdafx.h» #include «iostream.h» #include <conio.h>

int main()

{

float a; double b; long double c; int k;

a = 1; k = 0;

while (a != 0)

{

a = a / 2; k = k — 1;

}

cout << «Machine zero for float in (single precision) is 2 in «<< k

<< » degree» << endl;

b = 1; k = 0;

while (b != 0)

{

b = b / 2; k = k — 1;

}

cout << «Machine zero for float (double precision) is 2 in » << k << » degree» << endl;

c = 1; k = 0;

while (c != 0)

{

c = c / 2; k = k — 1;

}

cout << «Machine zero for float (quad precision) is 2 in » << k << » degree» << endl;

cout << «===================================================» << endl << endl;

cout << «===================================================» <<

endl;

a = 1; k = 0;

float fepsilon = 1;

while (a + fepsilon != a)

{

fepsilon = fepsilon / 2; k = k — 1;

}

cout << «Machine fepsilon for float (single precision) is 2 in » << k << » degree» << endl;

b = 1; k = 0;

double depsilon = 1;

while (b + depsilon != b)

{

depsilon = depsilon / 2; k = k — 1;

}

cout << «Machine fepsilon for float (double precision) is 2 in » << k << » degree» << endl;

c = 1; k = 0;

long double ldepsilon = 1;

while( c + ldepsilon != c)

{

ldepsilon = ldepsilon / 2; k = k — 1;

}

cout << «Machine fepsilon for float (quad precision) is 2 in » << k

<< » degree» << endl;

cout << «===================================================» << en dl << endl;

cout << «==========================================================

======» << endl;

cout << «Machine continium for float (single precision):» << endl; a = 1;

k = 2;

for (k = 1; k < 126; k++)

{

a = a * 2;

}

for (k = 127; k < 130; k++)

{

a = a * 2;

cout << » 2 in » << k << » degree = » << a << endl;

}

cout << «==========================================================

======» << endl;

cout << «Machine continium for float (double precision):» << endl;

b = 1; k = 2;

for (k = 1; k < 1022; k++)

{

b = b * 2;

}

for (k = 1023; k < 1026; k++)

{

b = b * 2;

cout << «2 in «<<k<<» degree = «<<b<<endl;

}

cout << «==========================================================

======» << endl;

cout << «Machine continum for float (quad precision):» << endl;

c = 1; k = 2;

for (k = 1; k < 1023; k++)

{

c = c * 2;

}

for (k = 1024; k < 1026; k++)

{

c = c * 2;

cout << «2 in » << k << » degree = » << c << endl;

}

cout << «================================================================» << endl;

getch(); return 0;

}

Вычисление на компиляторе Pascal:

1.Machine continuum in single: 6.80564733841877E+38

2.Machine continuum in double: 3.595386972463181E+308

3.Machine continuum in quad: 2.3794629907144635301E+4932

Текст программы:

program Null_Delphi; {$APPTYPE CONSOLE}

uses SysUtils;

Var

a, sepsilon : single;

b, depsilon : double;

c, eepsilon : extended;

k, choice : integer;

Begin Writeln(‘===================================================’);

a := 1; k := 0;

while a <> 0 do begin

a := a / 2; k := k — 1;

end;

Writeln(‘Machine zero in single mode is 2 in ‘,k,’ degree’);

b := 1; k := 0;

while b <> 0 do begin

b := b / 2; k := k — 1;

end;

Writeln(‘Machine zero in double mode is 2 in ‘,k,’ degree’);

c := 1; k := 0;

while c <> 0 do begin

c := c / 2; k : = k — 1;

end;

Writeln(‘Machine zero in extend mode is 2 in ‘,k,’ degree’);

Writeln(‘===================================================’);

Writeln;

Writeln(‘===================================================’);

a := 1; k := 0;

sepsilon := 1;

while a + sepsilon <> a do begin

sepsilon := sepsilon / 2; k := k — 1;

end;

Writeln(‘Machine epsilone in single mode is 2 in ‘,k,’ degree’); b := 1;

k := 0; Depsilon := 1;

while b + depsilon <> b do begin

depsilon := depsilon / 2; k := k — 1;

end;

Writeln(‘Machine epsilone in double mode is 2 in ‘,k,’ degree’);

c := 1; k := 0;

eepsilon: = 1;

while c + eepsilon <> c do begin

eepsilon := eepsilon / 2; k := k — 1;

end;

Writeln(‘Machine epsilone in extend mode is 2 in ‘,k,’ degree’);

Writeln(‘===================================================’);

Writeln;

Writeln(‘===================================================’);

Writeln(‘1: Machine continum in single’);

Writeln(‘2: Machine continum in double’);

Writeln(‘3: Machine continum in extend’);

Readln(choice);

if choice = 1 then begin

a := 1; k := 2;

while ioresult = 0 do

begin

a := a * 2; k := k + 1;

Write(chr(13));

Write(‘Machine continum in single mode is 2 in ‘,k,’

degree’);

end;

end;

if choice = 2 then begin

b := 1; k := 2;

while ioresult = 0 do begin

b := b * 2; k := k + 1;

Write(chr(13));

Write(‘Machine continum in double mode is 2 in ‘,k,’

degree’);

end;

end;

if choice = 3 then begin

c := 1; k := 2;

while ioresult = 0 do begin

c := c * 2; k := k + 1;

Write(chr(13));

Write(‘Machine continum in extend mode is 2 in ‘,k,’

degree’);

end;

end;

readln;

End.

Вывод: Полученные результаты соответствуют по своим порядкам теоретически возможным значениям.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Question book-2.svg

Эта статья нуждается в дополнительных источниках для улучшения проверяемости.
Вы можете помочь улучшить эту статью, добавив ссылки на авторитетные источники.
Не подтверждённая источниками информация может быть поставлена под сомнение и удалена.

Машинный нуль (англ. computer zero) — числовое значение, меньше которого невозможно задавать точность для любого алгоритма, возвращающего вещественные числа. Абсолютное значение «машинного нуля» зависит от разрядности сетки применяемой ЭВМ, от принятой в конкретном трансляторе точности представления вещественных чисел и от значений, используемых для оценки точности. [1]

В языке Си существуют предельные константы FLT_EPSILON и DBL_EPSILON называемые «машинными нулями» относительно вещественного значения 1.0. FLT_EPSILON — максимальное значение типа float и имеет значение 1E-5, DBL_EPSILON — максимальное значение типа double и имеет значение 1E-16. Сумма каждого из этих значений со значение 1.0 не отличается от 1.0.

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

При представлении чисел со знаком в обратных двоичных кодах существуют проблема[уточнить] наличия двух обратных кодов числа 0: «положительный нуль» и «отрицательный нуль».

Пример

Пример вычисления машинного эпсилона (не путать с машинным нулём) на языке Си.

#include <stdio.h>
int main()
{
   float e,e1; /* e1 - вспомогательная переменная */
   int k=0; /* k - счетчик итераций */
   e=1.0;
   do
   {
      e=e/2.0;
      e1=e+1.0;
      k++;
   } while (e1>1.0);
   printf("Число делений на 2: %dn",k);
   printf("Машинный эпсилон: %en",e);
   return 0;
}

Пример на языке C++.

#include <iostream>
#include <stdint.h>
#include <iomanip>
 
template<typename float_t, typename int_t>
float_t machine_eps()
{
        union
        {
                float_t f;
                int_t   i;
        } one, one_plus, little, last_little;
 
        one.f    = 1.0;
        little.f = 1.0;
        last_little.f = little.f;
 
        while(true)
        {
                one_plus.f = one.f;
                one_plus.f += little.f;
 
                if( one.i != one_plus.i )
                {
                        last_little.f = little.f;
                        little.f /= 2.0;
                }
                else
                {
                        return last_little.f;
                }
        }
}
 
int main()
{
        std::cout << "machine epsilon:n";
        std::cout << "float: " << std::setprecision(18)<< machine_eps<float, uint32_t>() << std::endl;
        std::cout << "double: " << std::setprecision(18) << machine_eps<double, uint64_t>() << std::endl;
}

Пример на Python

def machineEpsilon(func=float):
    machine_epsilon = func(1)
    while func(1)+func(machine_epsilon) != func(1):
        machine_epsilon_last = machine_epsilon
        machine_epsilon = func(machine_epsilon) / func(2)
    return machine_epsilon_last

Вывод может быть таким (с использованием IPython):

In [1]: machineEpsilon(int)
Out[1]: 1
In [2]: machineEpsilon(float)
Out[2]: 2.2204460492503131e-16
In [3]: machineEpsilon(complex)
Out[3]: (2.2204460492503131e-16+0j)

См. также

  • −0 (программирование)

Ссылки

  1. Подбельский В.В., Фомин С.С. Программирование по на языке Си: Учеб.пособие.Москва:Изд-во Финансы и статистика,2003.

Понравилась статья? Поделить с друзьями:

Не пропустите также:

  • Как найти человека в архивах украины
  • Есть фото как найти имя человека
  • Как составить звуковую модель слова белка
  • Как нашли ускорение свободного падения на луне
  • Как найти подкову на голове

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии