取 C 中两个有符号数的平均值
假设我们有 x 和 y,并且在 C 中都是有符号整数,我们如何找到两者之间最准确的平均值?
我更喜欢一个不利用任何机器/编译器/工具链特定工作的解决方案。
我想出的最好的方法是:(a / 2) + (b / 2) + !!(a % 2) * !!(b %2)
有没有更有效的解决方案准确的?快点?更简单吗?
如果我们先验地知道其中一个是否大于另一个怎么办?
谢谢。
D
编者注:请注意,当输入值接近 C int
类型的最大绝对界限时,OP 期望得到的答案不会受到整数溢出的影响。这在原始问题中没有说明,但在给出答案时很重要。
Let us say we have x and y and both are signed integers in C, how do we find the most accurate mean value between the two?
I would prefer a solution that does not take advantage of any machine/compiler/toolchain specific workings.
The best I have come up with is:(a / 2) + (b / 2) + !!(a % 2) * !!(b %2)
Is there a solution that is more accurate? Faster? Simpler?
What if we know if one is larger than the other a priori?
Thanks.
D
Editor's Note: Please note that the OP expects answers that are not subject to integer overflow when input values are close to the maximum absolute bounds of the C int
type. This was not stated in the original question, but is important when giving an answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
接受答案(4 年)后,
我期望函数
intaverage_int(int a, int b)
为:1. 针对
a
和b
的所有组合,在[INT_MIN..INT_MAX]
的整个范围内进行操作。2. 与
(a+b)/2
获得相同的结果,就像使用更广泛的数学一样。当int2x 存在,@Santiago Alessandri 方法效果很好。
否则 @AProgrammer 的变体:
注意:不需要更广泛的数学。
具有更多测试的 解决方案,但没有
%
以下所有解决方案“有效”到 1 以内
(a+b)/2
当没有发生溢出时,但我希望找到一个与所有int< 匹配的
(a+b)/2
/代码>。@Santiago Alessandri 只要
int
的范围小于int
的范围,解决方案就可以工作。 code>long long - 通常就是这种情况。@AProgrammer(接受的答案)大约有 1/4 的时间无法匹配
(a+b)/ 2.
.示例输入如a == 1, b == -2
@Guy Sirton,解决方案失败大约 1/8 的时间匹配
(a+b)/2
。示例输入如a == 1, b == 0
@R..,解决方案失败大约 1/4 的时间来匹配
(a+b)/2
。示例输入如a == 1, b == 1
@MatthewD,现在删除的解决方案失败大约 5/6 的时间匹配
(a+b)/2
。输入示例,例如a == 1, b == -2
After accept answer (4 yr)
I would expect the function
int average_int(int a, int b)
to:1. Work over the entire range of
[INT_MIN..INT_MAX]
for all combinations ofa
andb
.2. Have the same result as
(a+b)/2
, as if using wider math.When int2x exists, @Santiago Alessandri approach works well.
Otherwise a variation on @AProgrammer:
Note: wider math is not needed.
A solution with more tests, but without
%
All below solutions "worked" to within 1 of
(a+b)/2
when overflow did not occur, but I was hoping to find one that matched(a+b)/2
for allint
.@Santiago Alessandri Solution works as long as the range of
int
is narrower than the range oflong long
- which is usually the case.@AProgrammer, the accepted answer, fails about 1/4 of the time to match
(a+b)/2
. Example inputs likea == 1, b == -2
@Guy Sirton, Solution fails about 1/8 of the time to match
(a+b)/2
. Example inputs likea == 1, b == 0
@R.., Solution fails about 1/4 of the time to match
(a+b)/2
. Example inputs likea == 1, b == 1
@MatthewD, now deleted solution fails about 5/6 of the time to match
(a+b)/2
. Example inputs likea == 1, b == -2
如果
(a^b)<=0
你可以直接使用(a+b)/2
而不必担心溢出。否则,请尝试
(a-(a|b)+b)/2+(a|b)/2
。-(a|b)
的大小至少与a
和b
一样大,并且具有相反的符号,因此可以避免溢出。我很快就这样做了,所以可能会出现一些愚蠢的错误。请注意,这里没有特定于机器的黑客。
所有行为完全由 C 标准决定,事实上它需要有符号值的补码、补码或符号数值表示,并指定按位运算符对逐位表示进行处理。不,a|b
的相对大小取决于表示形式...编辑: 您还可以使用
a+(ba)/2< /code> 当它们具有相同的符号时。请注意,这会偏向
a
。您可以反转它并偏向b
。另一方面,如果我没有弄错的话,我上面的解决方案会偏向于零。另一种尝试:一种标准方法是
(a&b)+(a^b)/2
。在二进制补码中,无论符号如何,它都可以工作,但我相信如果a
和b
具有相同的符号,它也可以在补码或符号量值中工作。介意检查一下吗?If
(a^b)<=0
you can just use(a+b)/2
without fear of overflow.Otherwise, try
(a-(a|b)+b)/2+(a|b)/2
.-(a|b)
is at least as large in magnitude as botha
andb
and has the opposite sign, so this avoids the overflow.I did this quickly off the top of my head so there might be some stupid errors. Note that there are no machine-specific hacks here.
All behavior is completely determined by the C standard and the fact that it requires twos-complement, ones-complement, or sign-magnitude representation of signed values and specifies that the bitwise operators work on the bit-by-bit representation.Nope, the relative magnitude ofa|b
depends on the representation...Edit: You could also use
a+(b-a)/2
when they have the same sign. Note that this will give a bias towardsa
. You can reverse it and get a bias towardsb
. My solution above, on the other hand, gives bias towards zero if I'm not mistaken.Another try: One standard approach is
(a&b)+(a^b)/2
. In twos complement it works regardless of the signs, but I believe it also works in ones complement or sign-magnitude ifa
andb
have the same sign. Care to check it?编辑:由@chux修复的版本 - 恢复莫妮卡:
原始答案(如果它没有被接受,我会删除它)。
似乎是最简单的一种,符合对实现特征没有假设的要求(它依赖于 C99,它将 / 的结果指定为“向 0 截断”,而它依赖于 C90 的实现)。
它的优点是无需测试(因此无需昂贵的跳转),并且所有除法/余数均除以 2,因此编译器可以使用位旋转技术。
Edit: version fixed by @chux - Reinstate Monica:
Original answer (I'd have deleted it if it hadn't been accepted).
Seems the simplest one fitting the bill of no assumption on implementation characteristics (it has a dependency on C99 which specifying the result of / as "truncated toward 0" while it was implementation dependent for C90).
It has the advantage of having no test (and thus no costly jumps) and all divisions/remainder are by 2 so the use of bit twiddling techniques by the compiler is possible.
对于无符号整数,平均值是 (x+y)/2 的下限。但对于有符号整数,同样会失败。对于总和为奇数的整数,此公式失败,因为它们的下限比平均值小 1。
您可以在 Hacker's Delight 中阅读更多内容,第 2.5 节
计算 2 个有符号整数的平均值的代码,无需溢出是
我已经使用 Z3 SMT 求解器 检查了它的正确性
For unsigned integers the average is the floor of (x+y)/2. But the same fails for signed integers. This formula fails for integers whose sum is an odd -ve number as their floor is one less than their average.
You can read up more at Hacker's Delight in section 2.5
The code to calculate average of 2 signed integers without overflow is
I have checked it's correctness using Z3 SMT solver
一些观察可能会有所帮助:
“最准确”对于整数来说不一定是唯一的。例如,对于 1 和 4,2 和 3 是同样“最准确”的答案。从数学上来说(不是 C 整数):
让我们尝试将其分解:
您到底想优化什么?不同的处理器架构可能有不同的最佳解决方案。例如,在代码中用 AND 替换乘法可能会提高性能。另外,在二进制补码架构中,您可以简单地(a & b & 1)。
我只是要扔掉一些代码,看起来不会太快,但也许有人可以使用和改进:
Just a few observations that may help:
"Most accurate" isn't necessarily unique with integers. E.g. for 1 and 4, 2 and 3 are an equally "most accurate" answer. Mathematically (not C integers):
Let's try breaking this down:
What are you trying to optimize exactly? Different processor architectures may have different optimal solutions. For example, in your code replacing the multiplication with an AND may improve performance. Also in a two's complement architecture you can simply (a & b & 1).
I'm just going to throw some code out, not looking too fast but perhaps someone can use and improve:
我会这样做,将两者都转换为 long long(64 位有符号整数),将它们相加,这不会溢出,然后将结果除以 2:
如果您想要小数部分,请将其存储为双精度。
请务必注意,结果将适合 32 位整数。
如果您使用最高等级的整数,那么您可以使用:
I would do this, convert both to long long(64 bit signed integers) add them up, this won't overflow and then divide the result by 2:
If you want the decimal part, store it as a double.
It is important to note that the result will fit in a 32 bit integer.
If you are using the highest-rank integer, then you can use:
该答案适合任意数量的整数:
此测试预计 avg == 5.0
This answer fits to any number of integers:
expects avg == 5.0 for this test