如何使用位运算符将任何负值转换为零?

huangapple go评论142阅读模式
英文:

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.sizeint32 类型,并且 ll.Size() 返回 ll.size。当大小为正数时,右移操作将会将 -ll.size 符号扩展为 -1,否则它将为0。

如果 ll.sizeint64 类型,则将右移的位数改为63。如果 ll.sizeuint64 类型,并且大小永远不会超过 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 &gt;&gt; 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 &gt;&gt; 63)) // all ones if ll.size &gt;= (1 &lt;&lt; 63)
ll.size = ((ll.size - 1) &amp; mask) | ((ll.size + uint64(-int64(ll.size) &gt;&gt; 63)) &amp; ^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 := &amp;ll.node
	for (*lastNode).next != nil {
		lastNode = &amp;(*lastNode).next
	}
	*lastNode = nil
	ll.size -= 1
}

huangapple
  • 本文由 发表于 2021年6月4日 10:08:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/67830622.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定