Ошибка в функции MathCeil() или в логике округления данных

 

Билд терминала 211.

Замечена ошибка в функции MathCeil() при работе с типами данных (или в логике округления типов):


следующий код возвращает 16, а должно быть 15


void start() {
int i1 = 140579;
int i2 = 35579;

double d1 = i1 - i2;

Comment(MathCeil(d1 / 7000));
}


а если просто вывести

Comment(MathCeil(15));

что по сути дела одно и тоже, что и выше, то будет уже 15

 
Спасибо, будем разбираться.
 

Это не ошибка, а особенность плавающей арифметики. В результате деления числа с плавающей точкой d1 на 7000 получилось число 15.000000000000002

 
stringo писал (а):
В результате деления числа с плавающей точкой d1 на 7000 получилось число 15.000000000000002

Тогда всё правильно...

Функция MathCeil(х) возвращает числовое значение, представляющую наименьшее целое число, которое больше или равно x. Это будет 16.

 
stringo:

Это не ошибка, а особенность плавающей арифметики. В результате деления числа с плавающей точкой d1 на 7000 получилось число 15.000000000000002


Может и так, но ведь тип double в mql может содержать в себе максимально 8 цифр после запятой, и при выводе например этого числа в строку после точки получается как раз 8 нулей, и потом, почему получилось такое число? ведь должно быть ровно 15, во всяком случае на языке C++ и шарпе получается всегда все точно до скольки бы число не округлялось...
 
Na-Krul писал (а):
Может и так, но ведь тип double в mql может содержать в себе максимально 8 цифр после запятой
Не правильно.
//+------------------------------------------------------------------+
//| up to 16 digits after decimal point                              |
//+------------------------------------------------------------------+
string DoubleToStrMorePrecision(double number,int precision)
  {
   double rem,integer,integer2;
   double DecimalArray[17]={ 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0,  10000000.0, 100000000.0,
                             1000000000.0, 10000000000.0, 100000000000.0, 10000000000000.0, 100000000000000.0,
                             1000000000000000.0, 1000000000000000.0, 10000000000000000.0 };
   string intstring,remstring,retstring;
   bool   isnegative=false;
   int    rem2;
//----
   if(precision<0)  precision=0;
   if(precision>16) precision=16;
//----
   double p=DecimalArray[precision];
   if(number<0.0) { isnegative=true; number=-number; }
   integer=MathFloor(number);
   rem=MathRound((number-integer)*p);
   remstring="";
   for(int i=0; i<precision; i++)
     {
      integer2=MathFloor(rem/10);
      rem2=NormalizeDouble(rem-integer2*10,0);
      remstring=rem2+remstring;
      rem=integer2;
     }
//----
   intstring=DoubleToStr(integer,0);
   if(isnegative) retstring="-"+intstring;
   else           retstring=intstring;
   if(precision>0) retstring=retstring+"."+remstring;
   return(retstring);
  }
 
komposter:
Na-Krul писал (а):
Может и так, но ведь тип double в mql может содержать в себе максимально 8 цифр после запятой
Не правильно.

ошибся, я хотел сказать, что NormalizeDouble() округляет double максимум до 8 знаков после запятой, DoubleToStr() также выводит максимум 8 знаков после запятой...
 
stringo:

Это не ошибка, а особенность плавающей арифметики. В результате деления числа с плавающей точкой d1 на 7000 получилось число 15.000000000000002

плавающая она или ныряющая, должно быть (согласно законам математики) 15.00000000000000000000000000000000000000000000000000000000000000
 
Andy_Kon:
stringo:

Это не ошибка, а особенность плавающей арифметики. В результате деления числа с плавающей точкой d1 на 7000 получилось число 15.000000000000002

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

Вопрос погрешностей, сравнения чисел и результатов операций с вещественными числами постоянно всплывает на наших форумах. Вот одно из детальных объяснений погрешностей:

Mak 08.06.05 15:41

Еще как могут - и Вы это как раз показали своим примером. Вопрос "как это может появиться?" является теорией, а результат "математические(именно математические, а не только деление/умножение) операции с вещественными числами дают погрешности" - является суровой правдой жизни.
Погрешность для +/- с плавающей арифметикой будет +/- 1-2 эпсилон (или +/- 1-2 эпсилон * Макс(А,В)). Причем чем больше вычислений, тем больше накапливается ошибка.

Эпсилон - это примерно значение младшего разряда мантисы числа.
В языках даже такая константа бывает (в Си кажется есть).
Для double она примерно равна 10 в минус 14 степени.

Причин как минимум 2.

1. Вещественные числа в компе всегда представляются в виде дробного числа.
Т.е. в виде М * 2^Р, где М - мантиса, |M| < 1.0, а Р - порядок, целое со знаком.
Таким способом нельзя точно представить число, все что не влазит в мантису отбрасывается.

2. Числа представляются в двоичной системе, и если мы напишем 1.1 и в десятичной это выглядит просто, то в двоичной будет выглядеть намного сложнее - может получиться периодическая дробь например, которая будет округлена размером мантисы.

В результате может оказаться что 1.1 + 2.2 не равно 3.3
Мы записали в десятичной системе, а в компе они представляются в двоичной системе и в нейже выполняются операции.

Это общая проблема всех компов и всех языков.

Проверять вещественные числа на равенство всегда некорректно (за очень редким исключением).

Равенство можно проверить только с некоторой точностью.
И сделать это можно только так
MathAbs(X-Y) < precision

Для примера в начале ветки precision <= 0.0001
 
Renat:
Andy_Kon:
stringo:

Это не ошибка, а особенность плавающей арифметики. В результате деления числа с плавающей точкой d1 на 7000 получилось число 15.000000000000002

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

<?
$i1 = 140579;
$i2 = 35579;

$d1 = $i1 - $i2;

echo Ceil($d1 / 7000);

?>

Результат = 15.

Причём тут интел (или амд)?
 
А так?
<?
$i1 = 140579;
$i2 = 35579;
 
$d1 = $i1 - $i2;
 
echo(gettype($d1 / 7000));
echo Ceil($d1 / 7000);
 
?>
Причина обращения: