Как преобразовать 32-разрядное целое число, представляющее время в usec, в 32-разрядное целое число, представляющее время в виде двоичной дроби в секундах? - PullRequest
1 голос
/ 15 февраля 2009

POSIX использует struct timeval для представления временных интервалов.

struct timeval
{
    time_t   tv_sec;
    unsigned tv_usec;
};

GHS Integrity представляет Time следующим образом,

struct Time
{
    time_t Seconds;
    unsigned Fraction;
};

Например, 0,5 с представляется как 0x80000000, а 0,25 с - 0x40000000.

Как лучше всего конвертировать из timeval в Time?

(p.s. Ответ не в том, чтобы связать библиотеку POSIX с Integrity и использовать вызовы POSIX.)

Ответы [ 3 ]

5 голосов
/ 15 февраля 2009

Это необычный способ представления времени.

В любом случае, есть два простых способа сделать это в любом случае, если у вас есть 64-битные целые числа или числа с плавающей запятой (первые более вероятны во встроенной системе):

/* assuming long is 64-bit and int is 32-bit
   or in general long twice the size of int: */
Fraction = (long) tv_usec * UINT_MAX / 1000000        /* usecs to fraction */
tv_usec = (long) Fraction * 1000000 / UINT_MAX        /* fraction to usecs */

/* assuming floating points are available: */
Fraction = tv_usec * ((double) UINT_MAX / 1000000)    /* usecs to fraction */
tv_usec = Fraction * ((double) 1000000 / UINT_MAX)    /* fraction to usecs */

Очевидно, что оба являются только целочисленными приближениями, потому что большинство значений в одной шкале не могут быть представлены как целые числа в другой шкале. И в одном направлении вы можете потерять некоторую точность, потому что форма Fraction может представлять гораздо более мелкие времена - один шаг формы Fraction составляет менее 0,00024 микросекунд. Но это только в том случае, если ваш таймер может измерять эти значения, что маловероятно - большинство таймеров не могут измерять даже в масштабе микросекунд, а значение, которое вы видите в tv_usec, часто округляется.

Если ни 64-разрядные целые числа, ни числа с плавающей запятой не доступны, вы можете сделать это итеративно с дополнительной переменной. Я подумал, есть ли более простой (и менее дорогой, учитывая, что это временный код) способ сделать такое масштабирование, чем делать эквивалент итеративного 64-разрядного умножения и деления с двумя 32-разрядными целыми числами. Из двух идей, которые пришли мне в голову, одна не будет выполнять даже точное масштабирование и может дать результаты с отклонением до 9 бит, а та, которая компенсирует это, оказывается не дешевле. Если у меня возникнет что-то новое, я опубликую это здесь, но это интересная задача. У кого-нибудь еще есть хороший алгоритм или фрагмент? Возможно, с помощью небольшого предварительно вычисленного стола?

0 голосов
/ 15 февраля 2009

Я реализовал предложение @ Tom Alsberg (вариант double). Есть предостережения (сравните вывод для frac_t == uint32_t и frac_t == uint64_t).

#include <iomanip>  // setw()
#include <iostream>
#include <limits>

typedef unsigned frac_t;
const frac_t FRACTIONS_PER_SECOND = std::numeric_limits<frac_t>::max();

template <class Uint>
Uint fraction2usec(Uint fraction) {
  return static_cast<Uint>(fraction * 1e6 / FRACTIONS_PER_SECOND + 0.5);
}

template <class Uint>
Uint usec2fraction(Uint usec) {
  return static_cast<Uint>(usec / 1e6 * FRACTIONS_PER_SECOND + 0.5);
}

int main(void) {
  uintmax_t fractions[] = {
    0, 1, 0x10c6, 0x10c6f7a0b5edull,
    static_cast<uintmax_t>(FRACTIONS_PER_SECOND / 2. + 0.5),
    static_cast<uintmax_t>(FRACTIONS_PER_SECOND / 4. + 0.5), 
    FRACTIONS_PER_SECOND,
    FRACTIONS_PER_SECOND + 0x1ull,
  };
  const int w1 = 2*sizeof(uintmax_t) , w2 = 10;
  for (size_t i = 0; i < (sizeof(fractions) / sizeof(*fractions)); ++i)
    std::cout << std::hex << std::setw(w1) << fractions[i] << ": " 
          << std::dec << std::setw(w2) << fraction2usec(fractions[i]) 
          << ", "  << std::hex << std::setw(w1) 
          << usec2fraction(fraction2usec(fractions[i])) << "\n";
}

Выход (frac_t == uint32_t):

           0:          0,                0
           1:          0,                0
        10c6:          1,             10c7
10c6f7a0b5ed: 4294967297,     10c6f7a0b5ee
    80000000:     500000,         80000000
    40000000:     250000,         40000000
    ffffffff:    1000000,         ffffffff
   100000000:    1000000,         ffffffff

Выход (frac_t == uint64_t):

               0:          0,                0
               1:          0,                0
            10c6:          0,                0
    10c6f7a0b5ed:          1,     10c6f7a0b5ee
8000000000000000:     500000, 8000000000000000
4000000000000000:     250000, 4000000000000000
ffffffffffffffff:    1000000,                0
               0:          0,                0
0 голосов
/ 15 февраля 2009

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

Time t;
u64 s = 1000000 * t.Seconds + 
 u64(1000000 * reinterpret_cast<double>(0x3FF0000000000000|((u64)t.Fraction>>12)))
timeval tv;
tv.tv_sec = s / 1000000
tv.tv_usec = s % 1000000

Это foobar, но он действительно работает ... вам понадобятся 64-битные целые числа и двойные числа с плавающей запятой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...