【问题标题】:Divide a 48-bit number which is composed of a 16bit and a 32bit variable将一个 16 位和一个 32 位变量组成的 48 位数字相除
【发布时间】:2019-08-26 03:14:36
【问题描述】:

我正在为嵌入式微控制器 (STN1110) 开发软件。它没有 FPU 或 SFP 仿真。

有一个 SDK 方法以微秒为单位返回控制器的正常运行时间,分为两个变量:uint16 (msw) 和 uint32 (lsw)。

我想将此“对”转换为具有秒精度的单个 uint32 (/ 1000 / 1000)。

SDK API 文档声明如下:

uint32 TimeGet48 (uint16 * time_msw)
-----------------------------------------   
Read the current full 48-bit system time.

Returns all 48 bits of the current value of the system's 1MHz clock. The resolution of the returned 32 bit value is one microsecond, with the most significant word returned through the pointer passed as a parameter. The clock therefore wraps after approximately 8.9 years. That should be enough for most purposes.

Parameters:
- time_msw: A pointer to store the most significant word of the current system time
Returns:
- Lower 32 bits to the current system time.

这里的主要问题是,控制器最多只支持uint32,所以不支持uint64

我将一些我认为可能是解决方案的代码“粘合”在一起。不是,因为一旦 uint16 值 > 0 就会出现错误并且我无法找到它。

uint32 divide(uint32 time_in_us_lsb, uint16 time_in_us_msb, uint32 divisor)
{
    uint32 value1 = (uint32)time_in_us_msb & 0xFFFF;
    uint32 value2 = (uint32)(time_in_us_lsb >> 16) & 0xFFFF;
    uint32 value3 = (uint32)time_in_us_lsb & 0xFFFF;

    value2 += (value1 % divisor) << 16;
    value3 += (value2 % divisor) << 16;

    return (((value2 / divisor) << 16) & 0xFFFF0000) | ( value3 / divisor);
}

uint32 getUptimeSeconds() {
    uint32 time_in_us_lsb;
    uint16 time_in_us_msb;

    time_in_us_lsb = TimeGet48(&time_in_us_msb);   
    uint32 result = divide(time_in_us_lsb, time_in_us_msb, 100000);

    time_in_us_lsb = (uint32)result & 0xFFFFFFFF;
    time_in_us_msb = 0;

    return divide(time_in_us_lsb, time_in_us_msb, 10); 
}

注意:这里的速度不是问题,因为在应用程序生命周期内不会经常调用该函数。

编辑:添加了uint32 功能限制。

【问题讨论】:

  • 建议在这样的表达式中添加括号,这样读者就不必考虑运算符优先级:time_in_ms_lsb &gt;&gt; 16 &amp; 0xFFFF
  • 另外,为什么你的函数定义标记为extern
  • 会这样做的,谢谢你的建议。至于extern:这是断章取义,但应该与函数本身无关。我会删除它。
  • STN1110 看起来不是微控制器而是协议转换器IC?我在这里错过了什么?
  • 如果除数 (value1 / disivor) << 32 会溢出。如果除数 >= 65536 则 value1 % divisor == value1。除此之外,这看起来还可以。哪些输入值会给您错误的结果,您期望得到什么结果?

标签: c embedded


【解决方案1】:

.. 一旦 uint16 值 > 0 就会出现错误,我无法找到它。

鉴于“控制器的正常运行时间以微秒为单位”,至少出现一个错误

// uint32 result = divide(time_in_ms_lsb, time_in_ms_msb, 100000);
uint32 result    = divide(time_in_ms_lsb, time_in_ms_msb, 1000000);

简单地使用uint64_t 更有意义。

uint32 divide(uint32 time_in_ms_lsb, uint16 time_in_ms_msb, uint32 divisor) {
  uint64_t sum = ((uint64_t)time_in_ms_msb << 32) | divisor;
  return (uint32)(sum / divisor);
}

推荐:在代码中使用ms 微秒看起来像毫秒。为“μSeconds”建议us

【讨论】:

  • 我首先同意这个解决方案。它确实引入了 64 位数学库,但阅读起来更加清晰。如果您使用的平台甚至不支持 64 位数学(8051 的 keil)或者您正在为代码空间而烦恼,那么您可以寻找捷径或其他算法。
  • 关于usms:好点。关于uint64_t:微控制器不支持,应该在描述中更清楚地说明这一点。最大的类型是uint32Updated。
  • 请注意,所有符合 C99 或更高版本的编译器都支持 unsinged long long(至少 64 位),即使 μP 是 1 -bit Turing machine
【解决方案2】:

应该是:

uint32 getUptimeSeconds() {
    uint32 time_in_us_lsb;
    uint16 time_in_us_msb;

    time_in_us_lsb = TimeGet48(&time_in_us_msb);   
    uint32 result = divide(time_in_us_lsb, time_in_us_msb, 10000);

    time_in_us_lsb = (uint32)result & 0xFFFFFFFF;
    time_in_us_msb = 0;

    return divide(time_in_us_lsb, time_in_us_msb, 100); 
}

【讨论】:

  • 谢谢!!不知道我怎么会错过这个。使用/ 100000 除法达到 16 位限制。
【解决方案3】:

鉴于您的目标 CPU,您可能希望针对固定除数对其进行优化。在您的情况下,除以 1000000。

为此,您可以使用 a/1000000 = a * (2^64 / 1000000) / 2^64。 (2^64 / 1000000) 不是整数,但近似值就足够了。乘法可以通过一系列移位和加法来完成。您可以在 (2^64 / 1000000) 中添加一些错误以减少移位和添加。结果会稍微偏离,但不会太大。

正确完成后,您的结果将不会超过正确答案的 1 分。如果这很重要,您可以再次将结果乘以 1000000,进行比较,然后根据需要将结果调整为 1。

注意:您不必在上述近似值中使用 2^64。尝试 2^32 或 2^48 或其他数字,看看是否可以减少班次和增加次数。

PS:看看编译器对(a * uint64_t(1&lt;&lt;48 / 1000000)) &gt;&gt; 48做了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    • 2012-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 2015-09-30
    相关资源
    最近更新 更多