带负数的嵌入式除法
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_t
和int64_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");
您没有进行上述回复中提到的演员表;但是编译器 确实 执行了积分提升。对于您所观察到的,它是相同的差异。
在我多年的嵌入式编程经验中,我通常从不需要处理负数。这很疯狂,但它们在我的工作中并不经常出现。
我目前正在处理一个传感器读数,它可以是正数也可以是负数,需要按 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_t
和int64_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");
您没有进行上述回复中提到的演员表;但是编译器 确实 执行了积分提升。对于您所观察到的,它是相同的差异。