Я пытаюсь изменить масштаб временной метки (только дробная часть секунд) с наносекунд (единицы 10 ^ -9 секунд) до нижней половины метки времени NTP (единицы 2 ^ -32 секунд). Фактически это означает умножение на 4,2949673. Но мне нужно делать это без математики с плавающей точкой и без использования целых чисел больше 32 бит (на самом деле, я пишу это для 8-битного микроконтроллера, поэтому даже 32-битная математика стоит дорого, особенно деления).
Я придумал несколько алгоритмов, которые работают достаточно хорошо, но у меня нет реальных основ численных методов, поэтому я был бы признателен за любые предложения о том, как их улучшить, или любые другие алгоритмы, которые будут точнее и / или быстрее.
Алгоритм 1
uint32_t intts = (ns >> 16) * 281474 + (ns << 16) / 15259 + ns / 67078;
Первые две константы были выбраны для того, чтобы немного превышать, а не превышать, правильную цифру, и конечный коэффициент 67078 был определен эмпирически, чтобы исправить это. Производит результаты в пределах +/- 4 единиц NTP от правильного значения, которое +/- 1 нс - приемлемо, но остаточное значение изменяется с ns
. Я думаю, я мог бы добавить еще один термин.
Алгоритм 2
uint32_t ns2 = (2 * ns) + 1;
uint32_t intts = (ns2 << 1)
+ (ns2 >> 3) + (ns2 >> 6) + (ns2 >> 8) + (ns2 >> 9) + (ns2 >> 10)
+ (ns2 >> 16) + (ns2 >> 18) + (ns2 >> 19) + (ns2 >> 20) + (ns2 >> 21)
+ (ns2 >> 22) + (ns2 >> 24) + (ns2 >> 30) + 3;
Основано на двоичном расширении 4.2949673 (фактически на двоичном расширении 2.14748365, поскольку я начинаю с удвоения и добавления одного для выполнения округления). Возможно, быстрее, чем алгоритм 1 (я еще не получил тесты). +3 было определено эмпирически, чтобы исключить недолёт от усечения всех этих младших битов, но он не выполняет наилучшую возможную работу.