我需要对这个按位拼图做一些解释
I need a little explaination on this bitwise puzzle
我已经在这个按位拼图上卡了几个小时了。我在 google 上寻找解决这个问题的好方法,看看其他人是如何解决它的,我遇到了这个解决方案。我想弄清楚它是如何工作的。我唯一不明白的是舍入过程和丢失的位?丢失了哪些位?
非常感谢有人向我解释这个解决方案。
提前谢谢大家,Yosemite:)
/*
* ezThreeFourths - multiplies by 3/4 rounding toward 0,
* Should exactly duplicate effect of C expression (x*3/4),
* including overflow behavior.
* Examples: ezThreeFourths(11) = 8
* ezThreeFourths(-9) = -6
* ezThreeFourths(1073741824) = -268435456 (overflow)
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 3
*/
int ezThreeFourths(int x) {
/*
*sets y to make up for the lost bits while rounding. Multiplies
*by three by adding x 3 times. Records the sign since
* what is added will be different depending on whether the sign
*is positive or negative and adds this to to the number. Then
*divedes by 8 by shifting.
*/
int y = 3;
x =x+x+x;
int z = x>>31;
z = y & z;
x = z + x;
x = x>>2;
return x;
}
这是link我从哪里得到的:link个人没有在文件中标记他的名字所以我不知道他的名字
这里的所有工作只是为了匹配 iso C 定义除法以向零舍入的事实。乘以 3/4 的更简单的版本是乘以 3 然后移动 2:
return (x+x+x) >> 2;
不幸的是,负值有 wrong behaviour(它降低了值而不是向零四舍五入)。
为了解决这个问题,我们需要在负数项上加上 3*,然后再平移 2 位以通过截断模拟舍入。我们是这样做的:
int z = x >> 31; // Arithmetic right shift extracts sign mask (0xffffffff or 0x0)
int y = 3 & z; // 3 for negative values, 0 for nonnegative
return (x + x + x + y) >> 2; // Multiply and divide with rounding
为了更正确,我们应该考虑到我们并不知道整数的大小是 32,通过像这样更改代码:
int three_fourths(int x) {
return (x + x + x + (3 & (x >> (8 * sizeof(x) - 1)))) >> 2;
}
您可以看到这个最终实施的效果 here。
* 这是把地板分界线变成天花板分界线的成语。如果您有 x / N
向下舍入,您可以将表达式更改为 (x + N - 1) / N
以改为向上舍入。对于 N==4
,转换将是 x / 4
->(x + 3) / 4
未定义的行为
我们终于有了一些假设。当然 x + x + x
可以溢出并产生未定义的行为,这与 x * 3
没有什么不同(忽略问题作者暗示这具有相同的溢出行为,它会在某些实现上但不能保证)。
实现定义行为的主要部分是算术右移。以下是 C 标准在这个问题上的说法(ISO/IEC 9899:TC3 §6.5.7 ¶5):
The result of E1 >> E2
is E1
right-shifted E2
bit positions. If E1
has an unsigned type
or if E1
has a signed type and a nonnegative value, the value of the result is the integral
part of the quotient of E1
/ 2E2. If E1
has a signed type and a negative value, the
resulting value is implementation-defined.
因此,掩码提取只有在将任何负值右移一个小于 int 宽度的位置产生一个在所有位位置都为 1 的值时才会起作用。这在 common implementation 中适用于二进制补码系统(并且可能是您将要使用的任何系统),但标准在技术上不保证。
我已经在这个按位拼图上卡了几个小时了。我在 google 上寻找解决这个问题的好方法,看看其他人是如何解决它的,我遇到了这个解决方案。我想弄清楚它是如何工作的。我唯一不明白的是舍入过程和丢失的位?丢失了哪些位?
非常感谢有人向我解释这个解决方案。
提前谢谢大家,Yosemite:)
/*
* ezThreeFourths - multiplies by 3/4 rounding toward 0,
* Should exactly duplicate effect of C expression (x*3/4),
* including overflow behavior.
* Examples: ezThreeFourths(11) = 8
* ezThreeFourths(-9) = -6
* ezThreeFourths(1073741824) = -268435456 (overflow)
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 3
*/
int ezThreeFourths(int x) {
/*
*sets y to make up for the lost bits while rounding. Multiplies
*by three by adding x 3 times. Records the sign since
* what is added will be different depending on whether the sign
*is positive or negative and adds this to to the number. Then
*divedes by 8 by shifting.
*/
int y = 3;
x =x+x+x;
int z = x>>31;
z = y & z;
x = z + x;
x = x>>2;
return x;
}
这是link我从哪里得到的:link个人没有在文件中标记他的名字所以我不知道他的名字
这里的所有工作只是为了匹配 iso C 定义除法以向零舍入的事实。乘以 3/4 的更简单的版本是乘以 3 然后移动 2:
return (x+x+x) >> 2;
不幸的是,负值有 wrong behaviour(它降低了值而不是向零四舍五入)。
为了解决这个问题,我们需要在负数项上加上 3*,然后再平移 2 位以通过截断模拟舍入。我们是这样做的:
int z = x >> 31; // Arithmetic right shift extracts sign mask (0xffffffff or 0x0)
int y = 3 & z; // 3 for negative values, 0 for nonnegative
return (x + x + x + y) >> 2; // Multiply and divide with rounding
为了更正确,我们应该考虑到我们并不知道整数的大小是 32,通过像这样更改代码:
int three_fourths(int x) {
return (x + x + x + (3 & (x >> (8 * sizeof(x) - 1)))) >> 2;
}
您可以看到这个最终实施的效果 here。
* 这是把地板分界线变成天花板分界线的成语。如果您有 x / N
向下舍入,您可以将表达式更改为 (x + N - 1) / N
以改为向上舍入。对于 N==4
,转换将是 x / 4
->(x + 3) / 4
未定义的行为
我们终于有了一些假设。当然 x + x + x
可以溢出并产生未定义的行为,这与 x * 3
没有什么不同(忽略问题作者暗示这具有相同的溢出行为,它会在某些实现上但不能保证)。
实现定义行为的主要部分是算术右移。以下是 C 标准在这个问题上的说法(ISO/IEC 9899:TC3 §6.5.7 ¶5):
The result of
E1 >> E2
isE1
right-shiftedE2
bit positions. IfE1
has an unsigned type or ifE1
has a signed type and a nonnegative value, the value of the result is the integral part of the quotient ofE1
/ 2E2. IfE1
has a signed type and a negative value, the resulting value is implementation-defined.
因此,掩码提取只有在将任何负值右移一个小于 int 宽度的位置产生一个在所有位位置都为 1 的值时才会起作用。这在 common implementation 中适用于二进制补码系统(并且可能是您将要使用的任何系统),但标准在技术上不保证。