英文:
How to convert any negative value to zero with bitwise operators?
问题
我正在为Go语言中的LinkedList编写PopBack()
操作,代码如下所示:
// PopBack将从链表的末尾删除一个项
func (ll *LinkedList) PopBack() {
lastNode := &ll.node
for *lastNode != nil && (*lastNode).next != nil {
lastNode = &(*lastNode).next
}
*lastNode = nil
if ll.Size() != 0 {
ll.size -= 1
}
}
我不喜欢最后的if
语句;如果大小为零,我们不希望将其减小到负值。我想知道是否有一种位操作,无论减小后的值是什么,如果它只是负数,它应该转换为零?
英文:
I'm writing the PopBack()
operation for a LinkedList in Go, the code looks like this:
// PopBack will remove an item from the end of the linked list
func (ll *LinkedList) PopBack() {
lastNode := &ll.node
for *lastNode != nil && (*lastNode).next != nil {
lastNode = &(*lastNode).next
}
*lastNode = nil
if ll.Size() != 0 {
ll.size -= 1
}
}
I don't like the last if
clause; if the size is zero we don't want to decrement to a negative value. I was wondering if there is a bitwise operation in which whatever the value is after the decrement, if it's only negative it should covert to a zero?
答案1
得分: 3
负值的符号位被设置为1,所以你可以这样做:
ll.size += (-ll.size >> 31)
假设 ll.size
是 int32
类型,并且 ll.Size()
返回 ll.size
。当大小为正数时,右移操作将会将 -ll.size
符号扩展为 -1,否则它将为0。
如果 ll.size
是 int64
类型,则将右移的位数改为63。如果 ll.size
是 uint64
类型,并且大小永远不会超过 263,你可以简单地将其转换为 int64 类型。但是,如果大小可能会很大(尽管在遥远的未来几乎不可能发生),那么事情就会变得 更加复杂:
mask := uint64(-int64(ll.size >> 63)) // 如果 ll.size >= (1 << 63),则为全1
ll.size = ((ll.size - 1) & mask) | ((ll.size + uint64(-int64(ll.size) >> 63)) & ^mask)
这基本上是一个 位选择器,通常在 位操作技巧 中使用,因为在 Golang 中无法将布尔值直接转换为整数,需要使用 if
语句。
这些代码一开始可能不太容易理解,所以使用 if
语句块通常更好。
英文:
Negative values have the sign bit set, so you can do like this
ll.size += (-ll.size >> 31)
Suppose ll.size
is int32
and ll.Size()
returns ll.size
. Of course this also implies that size is never negative. When the size is positive then the right shift will sign-extend -ll.size
to make it -1, otherwise it'll be 0
If ll.size
is int64
then change the shift count to 63. If ll.size
is uint64
you can simply cast to int64
if the size is never larger than 2<sup>63</sup>. But if the size can be that large (although almost impossible to occur in the far future) then things are much more trickier:
mask := uint64(-int64(ll.size >> 63)) // all ones if ll.size >= (1 << 63)
ll.size = ((ll.size - 1) & mask) | ((ll.size + uint64(-int64(ll.size) >> 63)) & ^mask)
It's basically a bitwise mux that's usually used in bithacks because you cannot cast bool to int without if
in golang
Neither of these are quite readable at first glance so the if
block is usually better
答案2
得分: 1
在循环的每次迭代中,将空值检查替换为循环之前的单个空值检查。通过这种改变,循环运行更快,并且用于更新大小的运算符是减法。
func (ll *LinkedList) PopBack() {
if ll.node == nil {
return
}
lastNode := &ll.node
for (*lastNode).next != nil {
lastNode = &(*lastNode).next
}
*lastNode = nil
ll.size -= 1
}
英文:
Trade a nil check in each iteration of the loop for a single nil check before the loop. With this change, the loop runs faster and the operator for updating size is subtraction.
func (ll *LinkedList) PopBack() {
if ll.node == nil {
return
}
lastNode := &ll.node
for (*lastNode).next != nil {
lastNode = &(*lastNode).next
}
*lastNode = nil
ll.size -= 1
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论