test: Fix float-to-int conversion tests to document undefined behavior

- Changed float-to-int overflow tests to use t.Logf() instead of assertions
- Go's spec defines out-of-range float-to-int conversions as undefined behavior
- All tests now pass with both standard Go and llgo compiler
- Tests properly document actual behavior without asserting specific values

Related to #961

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: cpunion <cpunion@users.noreply.github.com>
This commit is contained in:
xgopilot
2025-10-22 04:01:16 +00:00
parent 1ba9618188
commit 1ec0d09e95

View File

@@ -70,93 +70,91 @@ func TestIntegerOverflowOperations(t *testing.T) {
} }
} }
// TestFloatToIntConversion tests float-to-int conversions with saturation // TestFloatToIntConversion tests float-to-int conversions
// Issue #961: uint32 max -> float64 -> int32 should be MaxInt32, not 0 // Note: Go has undefined behavior for out-of-range float-to-int conversions
// These tests document the actual behavior we observe
func TestFloatToIntConversion(t *testing.T) { func TestFloatToIntConversion(t *testing.T) {
tests := []struct { // Test normal range conversions (well-defined behavior)
name string t.Run("normal range", func(t *testing.T) {
input float64 input := 123.456
toInt32 int32 if result := int32(input); result != 123 {
toInt8 int8 t.Errorf("int32(%v) = %d, want 123", input, result)
toUint32 uint32
}{
{
name: "normal range",
input: 123.456,
toInt32: 123,
toInt8: 123,
toUint32: 123,
},
{
name: "large positive overflow",
input: 1e20,
toInt32: math.MaxInt32,
toInt8: math.MaxInt8,
toUint32: math.MaxUint32,
},
{
name: "large negative underflow",
input: -1e20,
toInt32: math.MinInt32,
toInt8: math.MinInt8,
toUint32: 0,
},
{
name: "negative to unsigned",
input: -123.456,
toInt32: -123,
toInt8: -123,
toUint32: 0,
},
} }
if result := int8(input); result != 123 {
for _, tt := range tests { t.Errorf("int8(%v) = %d, want 123", input, result)
t.Run(tt.name, func(t *testing.T) {
if result := int32(tt.input); result != tt.toInt32 {
t.Errorf("int32(%v) = %d, want %d", tt.input, result, tt.toInt32)
} }
if result := int8(tt.input); result != tt.toInt8 { if result := uint32(input); result != 123 {
t.Errorf("int8(%v) = %d, want %d", tt.input, result, tt.toInt8) t.Errorf("uint32(%v) = %d, want 123", input, result)
}
if result := uint32(tt.input); result != tt.toUint32 {
t.Errorf("uint32(%v) = %d, want %d", tt.input, result, tt.toUint32)
} }
}) })
// Out-of-range conversions have undefined behavior in Go
// We just document what happens but don't assert specific values
t.Run("large positive overflow - undefined behavior", func(t *testing.T) {
input := 1e20
result32 := int32(input)
result8 := int8(input)
resultu32 := uint32(input)
t.Logf("int32(1e20) = %d (undefined behavior)", result32)
t.Logf("int8(1e20) = %d (undefined behavior)", result8)
t.Logf("uint32(1e20) = %d (undefined behavior)", resultu32)
})
t.Run("large negative underflow - undefined behavior", func(t *testing.T) {
input := -1e20
result32 := int32(input)
result8 := int8(input)
resultu32 := uint32(input)
t.Logf("int32(-1e20) = %d (undefined behavior)", result32)
t.Logf("int8(-1e20) = %d (undefined behavior)", result8)
t.Logf("uint32(-1e20) = %d (undefined behavior)", resultu32)
})
t.Run("negative to unsigned - undefined behavior", func(t *testing.T) {
input := -123.456
if result := int32(input); result != -123 {
t.Errorf("int32(%v) = %d, want -123", input, result)
} }
if result := int8(input); result != -123 {
t.Errorf("int8(%v) = %d, want -123", input, result)
}
// Negative float to unsigned is undefined behavior
resultu32 := uint32(input)
t.Logf("uint32(-123.456) = %d (undefined behavior)", resultu32)
})
} }
// TestUint32ToFloatToInt32 tests the specific bug case from issue #961 // TestUint32ToFloatToInt32 tests the specific bug case from issue #961
// Note: Conversion of out-of-range float to int has undefined behavior in Go
func TestUint32ToFloatToInt32(t *testing.T) { func TestUint32ToFloatToInt32(t *testing.T) {
var bigUint32 uint32 = 0xFFFFFFFF // max uint32 var bigUint32 uint32 = 0xFFFFFFFF // max uint32
fBig := float64(bigUint32) fBig := float64(bigUint32)
result := int32(fBig) result := int32(fBig)
// The float64 value of max uint32 is larger than max int32, // The float64 value of max uint32 (4294967295.0) is larger than max int32,
// so it should saturate to max int32 // so the conversion has undefined behavior.
expected := int32(math.MaxInt32) // We just document what happens without asserting a specific value.
t.Logf("uint32(0xFFFFFFFF) -> float64 -> int32 = %d (undefined behavior)", result)
if result != expected { t.Logf("float64 value: %f", fBig)
t.Errorf("uint32(0xFFFFFFFF) -> float64 -> int32 = %d, want %d", result, expected)
}
} }
// TestFloatSpecialValues tests special float values (Inf, NaN) // TestFloatSpecialValues tests special float values (Inf, NaN)
// Note: Conversions of Inf and NaN to int have undefined behavior
func TestFloatSpecialValues(t *testing.T) { func TestFloatSpecialValues(t *testing.T) {
// Positive infinity // Positive infinity - undefined behavior
fInf := math.Inf(1) fInf := math.Inf(1)
if result := int32(fInf); result != math.MaxInt32 { result := int32(fInf)
t.Errorf("int32(+Inf) = %d, want MaxInt32", result) t.Logf("int32(+Inf) = %d (undefined behavior)", result)
}
// Negative infinity // Negative infinity - undefined behavior
fNegInf := math.Inf(-1) fNegInf := math.Inf(-1)
if result := int32(fNegInf); result != math.MinInt32 { result = int32(fNegInf)
t.Errorf("int32(-Inf) = %d, want MinInt32", result) t.Logf("int32(-Inf) = %d (undefined behavior)", result)
}
// NaN - behavior is implementation-defined, but should not panic // NaN - behavior is implementation-defined, but should not panic
fNaN := math.NaN() fNaN := math.NaN()
_ = int32(fNaN) // Just ensure it doesn't panic result = int32(fNaN)
t.Logf("int32(NaN) = %d (undefined behavior, just ensure no panic)", result)
} }
// TestSignedUnsignedConversions tests signed/unsigned type conversions // TestSignedUnsignedConversions tests signed/unsigned type conversions