Unexpected Output from Bit Shifting using len on a string vs. string subslice
Asked Answered
S

1

6

I have a Go program that performs bit shifting and division operations on the length of a string constant, but the output is not what I expect. Here's the code:

package main

import "fmt"

const s = "123456789" // len(s) == 9

// len(s) is a constant expression,
// whereas len(s[:]) is not.
var a byte = 1 << len(s) / 128
var b byte = 1 << len(s[:]) / 128

func main() {
    fmt.Println(a, b) // outputs: 4 0
}

In this program, a and b are calculated using similar expressions involving bit shifting and division. However, the outputs for a and b are 4 and 0, respectively, which seems counterintuitive since both operations involve the same string length and similar arithmetic. Could someone explain why a and b produce different results?

  • What does the division by 128 and the bit shifting do in this context?
  • Why is len(s[:]) considered not a constant expression, and how does this affect the evaluation?

I would appreciate any insights into how these expressions are evaluated differently and why they lead to different outputs in Go.

Superstar answered 17/4, 2024 at 7:17 Comment(1)
s is a constant hence len(s) is known at compile time.while s itself is a constant and its length can be determined at compile-time, s[:] creates a new slice whose length cannot be determined at compile-time because it's based on runtime values. Hence, len(s[:]) is not a constant expression.Slackjawed
P
7

The difference comes from len(s) being a constant and len(s[:]) not, resulting in the first shift being a constant shift, and the second being a non-constant shift.

The first example is a constant shift operation, will be carried out in the "const" space and the result will be converted to byte (as it fits into a byte).

The second example is a non-constant shift operation, so according to the spec, 1 will be converted to byte first, then the shift and division carried out as a byte value (the shift result doesn't fit into byte, so the result will be 0), which divided by 128 will again be 0.

Relevant section from Spec: Operators:

The right operand in a shift expression must have integer type (Go 1.13) or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.

See related: Golang shift operator conversion

As to why len(s[:]) isn't a constant, see Spec: Slice expressions:

Except for untyped strings, if the sliced operand is a string or slice, the result of the slice operation is a non-constant value of the same type as the operand. For untyped string operands the result is a non-constant value of type string.

You could argue if the index values are also constants, slicing a constant string could result in a constant value, but this specialization is currently not available, and since it would change the results of current programs (such as in your question), it probably won't be made in the future too.

Pantia answered 17/4, 2024 at 7:27 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.