mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-09-21 05:27:58 +08:00
feat: Add mathematical functions ROUND, CEIL, FLOOR, ABS with comprehensive tests
- Implement ROUND with optional precision parameter
- Add CEIL function for rounding up to nearest integer
- Add FLOOR function for rounding down to nearest integer
- Add ABS function for absolute values with type preservation
- Support all numeric types (int32, int64, float32, double)
- Comprehensive test suite with 20+ test cases covering:
- Positive/negative numbers
- Integer/float type preservation
- Precision handling for ROUND
- Null value error handling
- Edge cases (zero, large numbers)
All tests passing ✅
This commit is contained in:
@@ -140,3 +140,113 @@ func (e *SQLEngine) Divide(left, right *schema_pb.Value) (*schema_pb.Value, erro
|
||||
func (e *SQLEngine) Modulo(left, right *schema_pb.Value) (*schema_pb.Value, error) {
|
||||
return e.EvaluateArithmeticExpression(left, right, OpMod)
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// MATHEMATICAL FUNCTIONS
|
||||
// ===============================
|
||||
|
||||
// Round rounds a numeric value to the nearest integer or specified decimal places
|
||||
func (e *SQLEngine) Round(value *schema_pb.Value, precision ...*schema_pb.Value) (*schema_pb.Value, error) {
|
||||
if value == nil {
|
||||
return nil, fmt.Errorf("ROUND function requires non-null value")
|
||||
}
|
||||
|
||||
num, err := e.valueToFloat64(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ROUND function conversion error: %v", err)
|
||||
}
|
||||
|
||||
// Default precision is 0 (round to integer)
|
||||
precisionValue := 0
|
||||
if len(precision) > 0 && precision[0] != nil {
|
||||
precFloat, err := e.valueToFloat64(precision[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ROUND precision conversion error: %v", err)
|
||||
}
|
||||
precisionValue = int(precFloat)
|
||||
}
|
||||
|
||||
// Apply rounding
|
||||
multiplier := math.Pow(10, float64(precisionValue))
|
||||
rounded := math.Round(num*multiplier) / multiplier
|
||||
|
||||
// Return as integer if precision is 0 and original was integer, otherwise as double
|
||||
if precisionValue == 0 && e.isIntegerValue(value) {
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_Int64Value{Int64Value: int64(rounded)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_DoubleValue{DoubleValue: rounded},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Ceil returns the smallest integer greater than or equal to the value
|
||||
func (e *SQLEngine) Ceil(value *schema_pb.Value) (*schema_pb.Value, error) {
|
||||
if value == nil {
|
||||
return nil, fmt.Errorf("CEIL function requires non-null value")
|
||||
}
|
||||
|
||||
num, err := e.valueToFloat64(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CEIL function conversion error: %v", err)
|
||||
}
|
||||
|
||||
result := math.Ceil(num)
|
||||
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_Int64Value{Int64Value: int64(result)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Floor returns the largest integer less than or equal to the value
|
||||
func (e *SQLEngine) Floor(value *schema_pb.Value) (*schema_pb.Value, error) {
|
||||
if value == nil {
|
||||
return nil, fmt.Errorf("FLOOR function requires non-null value")
|
||||
}
|
||||
|
||||
num, err := e.valueToFloat64(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FLOOR function conversion error: %v", err)
|
||||
}
|
||||
|
||||
result := math.Floor(num)
|
||||
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_Int64Value{Int64Value: int64(result)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Abs returns the absolute value of a number
|
||||
func (e *SQLEngine) Abs(value *schema_pb.Value) (*schema_pb.Value, error) {
|
||||
if value == nil {
|
||||
return nil, fmt.Errorf("ABS function requires non-null value")
|
||||
}
|
||||
|
||||
num, err := e.valueToFloat64(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ABS function conversion error: %v", err)
|
||||
}
|
||||
|
||||
result := math.Abs(num)
|
||||
|
||||
// Return same type as input if possible
|
||||
if e.isIntegerValue(value) {
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_Int64Value{Int64Value: int64(result)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Check if original was float32
|
||||
if _, ok := value.Kind.(*schema_pb.Value_FloatValue); ok {
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_FloatValue{FloatValue: float32(result)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Default to double
|
||||
return &schema_pb.Value{
|
||||
Kind: &schema_pb.Value_DoubleValue{DoubleValue: result},
|
||||
}, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user