带负数的嵌入式除法

Embedded division with negative numbers

在我多年的嵌入式编程经验中,我通常从不需要处理负数。这很疯狂,但它们在我的工作中并不经常出现。

我目前正在处理一个传感器读数,它可以是正数也可以是负数,需要按 0.006 缩放,保留符号。为了避免在运行时进行不必要的浮点计算,我有一个算法可以将其转换为分子和分母 (3/500)。对于正数,一切都按预期工作,但对于负数,情况如下:

Raw data:         -103
Multiplied by 3:  -309
Divided by 500:   36893488147419102

我想出了这个数字的来源,我有一个解决方法,但我宁愿相信数学就是数学。

这是相同的十六进制计算:

Raw data:         0xFFFFFFFFFFFFFF99
Multiplied by 3:  0xFFFFFFFFFFFFFECB
Divided by 500:   0x0083126E978D4FDE

在计算器 (SpeedCrunch) 中:

0xFFFFFFFFFFFFFECB/500 = 0x83126E978D4FDE.9D2F1A9FBE76C8B44

原始 36893488147419102 是 SpeedCrunch 结果的组成部分 0x83126E978D4FDE

我不想每次用负数除法时都必须保存符号,进行正除法,然后重新添加符号。这是怎么回事?

环境是 CortexM3 micro,GCC4.9.3 使用 c++11。计算是在 int64_t 上完成的,numerator/denominators 是 uint64_t.

编辑: 以下是对迈克尔评论的回应的代码片段:

int64_t data = -103;
uint64_t resolutionNumerator = 3;
uint64_t resolutionDenominator = 500;

data *= resolutionNumerator;
data /= resolutionDenominator;

具有相同宽度的有符号和无符号整数的运算,例如uint64_tint64_t,导致有符号操作数被转换为无符号操作数的类型。

因此这两个表达式是等价的:

  • (int64_t) -103 * (uint64_t) 3 / (uint64_t) 500
  • (uint64_t) -103 * (uint64_t) 3 / (uint64_t) 500

如果分子和分母使用有符号类型int64_t,结果将保留符号。

What's going on here?

当您将其从无符号类型转换为有符号类型时,前导 1 位保留用于 2 的补码表示中的负数。

可能能够通过将最终结果转换回有符号值来恢复预期结果。作为有符号值,前导 1 位将表示其负值。


我们只能说可能因为我们没有看到套路。迈克尔在评论中指出了同样的问题。

int64_t data = -103;
uint64_t resolutionNumerator = 3;
uint64_t resolutionDenominator = 500;

C/C++的整数提升规则是指有符号类型提升为无符号类型。这就是为什么 -1 > 1:

int i = -1;
unsigned int j = i;

if ( i > j )
    printf("-1 is greater than 1\n");

您没有进行上述回复中提到的演员表;但是编译器 确实 执行了积分提升。对于您所观察到的,它是相同的差异。