Неправильная линейная интерполяция с большими значениями x с использованием математики.Чистые цифры

#c# #linear-interpolation #math.net #numerics

#c# #линейная интерполяция #math.net #цифры

Вопрос:

Я пытаюсь использовать математику.ЧИСТЫЕ цифры для интерполяции ряда значений даты и времени. Я начал с линейной интерполяции, но получаю некоторые очень странные результаты.

Запуск этого теста:

 public class script{

public void check_numerics()
  {
    var ticks = DateTime.Now.Ticks;
    Console.WriteLine("Ticks: "   ticks);
    var xValues = new double[] { ticks, ticks   1000, ticks   2000, ticks   3000, ticks   4000, ticks   5000 };
    var yValues = new double[] {0, 1, 2, 3, 4, 5};
    var spline = Interpolate.LinearBetweenPoints(xValues, yValues);
    var ticks2 = ticks;
    for (int i = 0; i < 10; i  )
    {
      ticks2  = 500;
      Console.WriteLine(spline.Interpolate(ticks2));
    }
  }
}
  

Это дает:

 Ticks: 635385235576843379
0.5
1
1.5
2
2.42857142857143 // this should be 2.5
3
3.5
4
4.5
5
  

Обратите внимание, что 2.4285 довольно неверно. В другое время (другое значение тиков) другое значение будет «неправильным». Есть ли «ошибка» с большими значениями x в Math.NET или я ожидаю слишком многого?

Комментарии:

1. Это не будет полностью точным, учитывая, что double имеет только 15-17 с.ф., и вы превышаете это, поэтому double не может точно представлять значения, которые вы ему даете, но я бы подумал, что это будет ближе, чем это

2. Спасибо @JamesBarrass, так что вы не можете представить текущее время в двойном формате — полезно знать!

3. Не с такой точностью, миллисекунды, может быть, 10 микросекунд. Не намного меньше. Тики IIRC составляют около 100 наносекунд

Ответ №1:

Просто подтверждаю приведенные выше комментарии в качестве сопровождающего Math.ЧИСТЫЕ цифры:

Расстояние (эпсилон) между ближайшими числами этой величины, которые могут быть представлены с двойной точностью, равно 128:

 Precision.EpsilonOf(ticks); // 128
  

Это означает, что если вы добавите или вычтете 128/2-1 = 63 из этого числа, вы получите точно такое же число:

 long ticks = DateTime.Now.Ticks // 635385606515570758
((long)(double)ticks)           // 635385606515570816
((long)(63 (double)ticks))      // 635385606515570816
((long)(-63 (double)ticks))     // 635385606515570816
((long)(65 (double)ticks))      // 635385606515570944
((long)(-65 (double)ticks))     // 635385606515570688
  

Инкрементные шаги 500 очень близки к этим 128 и эффективно округляются до кратных 128 (например, 512), поэтому неудивительно, что будут некоторые артефакты, подобные этому.

Если вы уменьшите точность времени до миллисекунд, разделив такты на 10000, как предложил Джеймс, вы получите эпсилон 0,0078125 и точные результаты даже для шагов 1 вместо 500.

 Precision.EpsilonOf(ticks/10000); // 0.0078125