如何将表示时间(单位为 usec)的 32 位 int 转换为表示时间(以秒为单位的二进制分数)的 32 位 int?

发布于 2024-07-14 10:03:14 字数 454 浏览 3 评论 0原文

POSIX 使用 struct timeval 来表示时间间隔。

struct timeval
{
    time_t   tv_sec;
    unsigned tv_usec;
};

GHS 完整性按以下方式表示时间

struct Time
{
    time_t Seconds;
    unsigned Fraction;
};

例如,0.5 秒表示为0x80000000,0.25 秒表示为0x40000000

timeval 转换为 Time 的最佳方法是什么?

(ps 答案是不要将 POSIX 库链接到 Integrity 并使用 POSIX 调用。)

POSIX uses struct timeval to represent time intervals.

struct timeval
{
    time_t   tv_sec;
    unsigned tv_usec;
};

GHS Integrity represents Time in the following manner,

struct Time
{
    time_t Seconds;
    unsigned Fraction;
};

For example, 0.5sec is represented as 0x80000000 and 0.25sec is represented as 0x40000000.

What is the best way to convert from timeval to Time?

(p.s. The answer is not to link the POSIX library into Integrity and use POSIX calls.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

茶花眉 2024-07-21 10:03:14

这是一种不寻常的时间表示方式。

不管怎样,如果你有 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 位整数和浮点数均不可用,您可以使用额外的变量进行迭代。 我在想是否有一种更简单(并且更便宜,考虑到这是定时代码)的方法来进行这种缩放,而不是用两个 32 位整数进行迭代 64 位乘法和除法。 在我想到的两个想法中,其中一个不会进行精确的均匀缩放,并且可能会产生最多 9 位偏差的结果,而补偿这一点的想法结果并不便宜。 如果我想到了新的东西,我会将其发布在这里,但这是一个有趣的挑战。 还有其他人有好的算法或片段吗? 也许借助一个小的预先计算表?

This is an unusual way to represent time.

Anyway, there are two easy ways to do it either way if you have 64-bit integers or floating points (the former are more likely on an embedded system):

/* 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 */

Obviously both are only integer approximations, because most values in one scale cannot be represented as integers in the other scale. And in one direction you may be losing some precision because the Fraction form can represent much finer times - one increment of the Fraction form is less than 0.00024 microseconds. But that is only if your timer can actually measure those values which is not very likely - most timers cannot even measure at the scale of microseconds, and the value you see in tv_usec is often rounded.

If neither 64-bit integers nor floating points are available an option, you could do it iteratively with an extra variable. I was thinking if there is a simpler (and less expensive, considering that this is timing code) way to do such scaling than doing the equivalent of iterative 64-bit multiplication and division with two 32-bit integers. Of the two ideas that came to my mind, one would not do exact even scaling and may produce results that are by up to 9 bits off, and the one that compensates for that turns out not to be any cheaper. If something new comes up in my mind I will post it here, but this is an interesting challenge. Does anyone else have a good algorithm or snippet? Perhaps with the aid of a small precomputed table?

冰雪梦之恋 2024-07-21 10:03:14

您可能想阅读浮点表示形式,因为分数似乎是有效数的第一位。

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 位整数和双浮点数。

You might wanna read up on floating-point representation as Fraction seems to be the first bits of the significand.

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

This is foobar but it really works... you'll need 64-bit integers and double floating-point.

随风而去 2024-07-21 10:03:14

我已经实现了 @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

I've implemented @Tom Alsberg's suggestion (double variant). There are caveats (compare output for frac_t == uint32_t and 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";
}

Output (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

Output (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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文