带无符号分子的有符号除法
我正在尝试计算滚动平均值,并尝试获取和优化一点,我简化了计算,因此只有一个除法。当该值下降时,存在一个点,当前值降低到低于平均值。此时平均数会跳跃。我想这是因为除法是无符号的,而我的分子的符号位被解释为一个巨大的无符号数。我只是不确定需要在哪里强制转换 unsigned 以确保此问题不会再次出现。
unsigned int AverageUsage;
unsigned int TotalUsage;
unsigned int incCount;
AverageUsage = (TotalUsage - AverageUsage)/++incCount + AverageUsage;
AverageUsage 始终为正数,但是当 TotalUsage 低于 AverageUsage 时,我不确定除法会发生什么情况
AverageUsage = (signed int)(TotalUsage - AverageUsage)/++incCount + AverageUsage;
会将分子设置为有符号,但我不确定除法将如何发生。
AverageUsage = (signed int)((signed int)(TotalUsage - AverageUsage)/++incCount) + AverageUsage;
应该可以工作(我可以保证这个完整操作的结果永远不会为负),但我担心 incCount 达到“看起来”为负的值的情况。
有没有一个简单的解决方案希望:
- 不需要 if 语句
- 不需要 QWORD
谢谢!
I'm trying to calculate a rolling average, and to try and get and optimize a bit, I've simplified the calculation so there is only one division. When the value is decreasing, there is a point where the current value is lowered to less than the average. At this point the average jumps. I imagine this is because the division is unsigned, and my numerator's sign bit is interpreted as a massive unsigned number. I am just not sure where I need to cast unsigned to insure this problem doesn't reappear.
unsigned int AverageUsage;
unsigned int TotalUsage;
unsigned int incCount;
AverageUsage = (TotalUsage - AverageUsage)/++incCount + AverageUsage;
AverageUsage will always be positive, but when TotalUsage drops below AverageUsage, I'm not sure what to expect with the division
AverageUsage = (signed int)(TotalUsage - AverageUsage)/++incCount + AverageUsage;
Will set the numerator to signed, but I am not sure how the division will occur.
AverageUsage = (signed int)((signed int)(TotalUsage - AverageUsage)/++incCount) + AverageUsage;
Should work (I can guarantee the result of this full operation will never be negative), but I am worried about cases when incCount reaches a value that 'looks' negative.
Is there a simple solution to this that hopefully:
- Doesn't need an if statement
- Doesn't require QWORDs
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
C 二进制运算(包括除法)的一般规则是操作数都将转换为相同类型,即以下类型之一:
int
、unsigned int
、long
、无符号长
、intmax_t
、uintmax_t
、浮点
、双精度
代码>, <代码>长双。如果两个操作数都是该列表中的类型,则它们都将转换为后一个。如果两者都不是,它们都会被转换为int
所以在你的例子中:
如果
incCount
是unsigned int
,那么你的转换没有效果-- 减法将转换为有符号整型,然后再转换回无符号整型,然后进行无符号除法。如果您想要一个带符号的除法,您将需要:正如您所注意到的,如果 incCount 超过 INT_MAX,这可能会给您带来麻烦。
一般来说,用于除法的处理器指令仅指定一种类型,该类型用于两个操作数。当有不同类型除法的特殊指令时,通常是为了更大(双倍宽度)的被除数,而不是不同的符号。
The general rule of C binary ops (including division) is that the operands will both be converted to the same type, which is one of:
int
,unsigned int
,long
,unsigned long
,intmax_t
,uintmax_t
,float
,double
,long double
. If both operands are of types in that list, they'll both be converted to the later one. If neither is, they'll both be converted toint
So in your example:
if
incCount
isunsigned int
, then your cast has no effect -- the subtract will be converted to signed int and then right back to unisgned int and an unsigned division will be done. If you want a signed division, you'll need:which as you note may get you into trouble if incCount exceeds INT_MAX.
In general, processor instructions for division only specify one type, which is used for both operands. When there is a special instruction for division with differing types, its usually for a larger (double width) dividend, not a different signedness.
你有两个选择。
使用浮点数学
我认为无论如何你都想这样做以获得正确的平均值。
不存在混合浮点数/整数除法这样的东西。因此,分子和分母都将转换为浮点数。
分子或分母是有符号还是无符号并不重要。不存在无符号浮点数这样的东西。分母 incCount 将转换为浮点型,并进行完整的浮点除法。
使用整数除法并处理特殊情况
如果由于某种原因您想保留整数除法,则分子和分母必须是相同的有符号/无符号类型。
分子/分母都带符号
incCount 将转换为带符号的数字。如果它太大,那么它看起来就像一个负数,你的答案就会是错误的。您必须测试此溢出。
分子/分母都是无符号的
您必须使分子无符号并使用 if () 语句来处理两种情况: AverageUsage 和
TotalUsage>平均使用情况
。这里 incCount 可以使用整数位的全部范围,因为它将被视为无符号数。You have 2 options.
Use Floating Point Math
I think you want to do this to get a proper average anyway.
There is no such thing as a mixed floating/integer divide. So, both numerator and denominator will be converted to a floating point.
Whether the numerator or denominator is signed or unsigned then doesn't matter. There is no such thing as unsigned floating point. The denominator incCount will be converted to a floating point and full floating point division will be done.
Use Integer division and handle the special cases
If for some reason you want to stay with integer division, then both the numerator and denominator have to be the same signed/unsigned type.
Both Numerator/Denominator are signed
incCount will be converted to a signed number. If it is too large then it will look like a negative number and your answer will be wrong. You have to test for this overflow.
Both Numerator/Denominator are unsigned
You have to make the numerator unsigned and use a if () statement to handle the two cases:
TotalUsage < AverageUsage
andTotalUsage > AverageUsage
. Here incCount can use the full range of integer bits since it will be treated as an unsigned number.当然请注意,这不是标准平均值。标准平均值是:
假设(理想情况下)incCount 是一些有用的周期性增加值(例如秒)。
衰减平均值通常更类似于: http://donlehmanjr.com/Science/ 03%20Decay%20Ave/032.htm 如果我翻译正确的话是:
正如 Himadri 提到的,这些可能应该完成在浮点运算中。
Note of course that this is not a standard average. A standard average would be:
Assuming (ideally) that incCount is some useful periodically increasing value (like seconds).
A decaying average is typically implemented more like: http://donlehmanjr.com/Science/03%20Decay%20Ave/032.htm which if I have translated correctly is:
As Himadri mentioned, these should probably be done in floating point arithmetic.
如果 TotalUsage < 是可预见且有效的AverageUsage,那么这些变量是无符号类型是完全不合适的。总使用量 < AverageUsage 意味着 AverageUsage 可能为负(如果 TotalUsage < AverageUsage 这将是结果。如果“平均”的数据从不为负,则 TotalUsage < AverageUsage 在算术上不可能为真。
如果 TotalUsage < AverageUsage,则 TotalUsage < AverageUsage 不可能为真。 AverageUsage 无效,那么如果它为真,则表明代码中存在错误或算术溢出,您可以通过实施断言来防止这种可能性;作为在发布版本中删除的宏。如果发生断言,则输入数据无效,或者发生溢出,在后一种情况下,数据类型太小,或者是
long long
、unsigned long long
或double
即使使用强制转换,如果 TotalUsage < AverageUsage 为 true,则表达式的结果在算术上为负数,但最终结果为负。分配给无符号类型,所以结果仍然是 最终的结论
是 TotalUsage < AverageUsage 永远不可能为真,或者您的数据具有不适当的类型。几乎可以肯定,解决方案不是任何类型的类型转换。
我的建议通常是始终对要执行算术的变量使用有符号类型。这是因为混合有符号/无符号算术的语言语义有些晦涩且容易被误解,并且中间操作可能会生成负值。即使变量的负值在语义上没有意义,我仍然主张在所有情况下使用有符号类型,只要此类类型的正值范围足以避免溢出,以及它还不够的情况。尽可能使用更大的类型,而不是采用相同大小的无符号类型。此外,如果需要对无符号类型进行算术运算,则所有操作数都应该是无符号的(包括文字),并且任何中间操作都不应导致下溢或溢出。
If it is foreseeable and valid for TotalUsage < AverageUsage, then it is entirely inappropriate for these variables to be of unsigned type. TotalUsage < AverageUsage would imply that AverageUsage could then be negative (which would be the result if TotalUsage < AverageUsage. If the data being 'averaged' is never negative, then it is arithmetically impossible for TotalUsage < AverageUsage to be true.
If TotalUsage < AverageUsage is not valid, then for it to be true would indicate an error in your code or an arithmetic overflow. You might guard against that possibility with an assert; perhaps one implemented as a macro that is removed in a release build. If the assert occurs then either the input data was invalid, or an overflow occurred, in the latter case the data type is too small, and either a
long long
,unsigned long long
, or adouble
would be appropriate.Even with casting, if TotalUsage < AverageUsage is true then the result of the expression is arithmetically negative, but ultimately assigned to an unsigned type, so the result will still be incorrect.
The ultimate conclusion then is either that TotalUsage < AverageUsage can never be true, or your data has inappropriate type. The solution is almost certainly not any kind of type cast.
My advice is generally to always use a signed type for variables on which arithmetic will be performed. This is because the language semantics of mixed signed/unsigned arithmetic are somewhat arcane and easily misunderstood, and because intermediate operations may generate otherwise negative values. Even if a negative value for the variable is semantically meaningless, I would still advocate the use of signed types in all cases where the positive range of such a type remains sufficient to avoid overflow, and where it is not sufficient. to use a larger type where possible rather than resort to an unsigned type of the same size. Further, where arithmetic operations on unsigned types is required, then all operands should be unsigned (including literals), and no intermediate operation should result under or overflow.
您真的/需要/滚动平均,还是可以使用其他低通滤波器?单极点(有时称为“alpha”)滤波器可能适合您:
其中
alpha
介于 0 和 0.9999 之间......alpha
越接近 1,过滤器“更慢”您可以轻松地以浮点形式执行此操作,也可以非常简单地以整数形式执行此操作。
Do you truly /need/ a rolling-average, or can you use some other low-pass filter? A single-pole (sometimes called an "alpha") filter might suit you:
where
alpha
is between 0 and 0.9999....The closer
alpha
is to 1, the "slower" the filter isYou can do this in floating point for ease, or in integers quite straightforwardly.