#979·sonic

ast.NewAny numeric accessors lose stored values

Author: dajiaohuangCreated Aug 22, 2026Updated Aug 22, 2026

Describe the bug

Numeric values stored by ast.NewAny are not preserved by the loose Number and String accessors:

  • Number converts every non-zero integer or float to "1" and every zero to "0".
  • String converts wide integers through the platform int, and formats float32 with 64-bit precision.

This conflicts with the numeric loose-casting behavior introduced in #294 and can silently change values.

Affected commit: 2a36d6da63e25b9080cc4e11398bd5b3512dfc2a (current main).

To Reproduce

go
package main

import (
    "fmt"

    "github.com/bytedance/sonic/ast"
)

func main() {
    numberNode := ast.NewAny(42)
    uintNode := ast.NewAny(^uint64(0))
    floatNode := ast.NewAny(float32(1.1))

    number, _ := numberNode.Number()
    uintText, _ := uintNode.String()
    floatText, _ := floatNode.String()

    fmt.Printf("number=%q uint64=%q float32=%q\n", number, uintText, floatText)
}

Output on amd64:

number="1" uint64="-1" float32="1.100000023841858"

Expected behavior

The accessors should preserve the stored numeric value and use the source type's width:

number="42" uint64="18446744073709551615" float32="1.1"

Sonic version:

Current main at 2a36d6da63e25b9080cc4e11398bd5b3512dfc2a.

Environment:

go version go1.26.3 windows/amd64
GOOS=windows
GOARCH=amd64
CGO_ENABLED=0

Additional context

The _V_ANY branch of Number passes numeric values to the boolean-only castNumber(v != 0) helper. The corresponding String branch uses strconv.Itoa(int(v)) for signed and unsigned wide types and uses bit size 64 for float32. Existing tests use only the value 1, which masks all three problems.