From c2528014a6c621dca9d46eb70a13db9038e8e5d0 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 24 May 2026 10:29:48 +0300 Subject: fix(rpn): use NewRatFromString in RationalMode to preserve precision In pushLiteral, use NewRatFromString(token) instead of NewRat(ParseFloat(token)) when mode is RationalMode. This preserves the full precision of the original numeric string by parsing it directly into big.Rat, avoiding the float64 intermediate that loses precision. In ResultStack, replace NewNumber(val, mode) with explicit mode dispatch for variable references. Note: variables stored as float64 still lose precision at storage time, but the push path is now consistent. --- internal/rpn/rpn_ops.go | 6 +++++- internal/rpn/rpn_parse.go | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/rpn/rpn_ops.go b/internal/rpn/rpn_ops.go index caf1e87..9104eea 100644 --- a/internal/rpn/rpn_ops.go +++ b/internal/rpn/rpn_ops.go @@ -49,7 +49,11 @@ func (r *RPN) ResultStack(tokens []string) (string, error) { // Check if it's a variable reference (push its value) val, exists := r.vars.GetVariable(token) if exists { - stack.Push(NewNumber(val, mode)) + if mode == RationalMode { + stack.Push(NewRat(val)) + } else { + stack.Push(NewFloat(val)) + } } else { return "", fmt.Errorf("unknown token '%s'", token) } diff --git a/internal/rpn/rpn_parse.go b/internal/rpn/rpn_parse.go index 9906f98..4035e3d 100644 --- a/internal/rpn/rpn_parse.go +++ b/internal/rpn/rpn_parse.go @@ -396,6 +396,8 @@ func (r *RPN) handleOperator(stack *Stack, token string, tokenIndex int) (string stack.Push(NewNumber(val, r.ops.GetMode())) return "", nil } + // Note: variable and constant values are stored as float64, so precision + // is already lost at storage time; NewNumber here preserves that behavior. // Handle standard operators (common logic extracted for DRY) // This must be done BEFORE pushing Symbol for unknown identifiers, @@ -482,11 +484,20 @@ func (r *RPN) pushLiteral(stack *Stack, token string) (bool, error) { } // Check if it's a number - if num, err := strconv.ParseFloat(token, 64); err == nil { + if _, err := strconv.ParseFloat(token, 64); err == nil { if stack.Len() >= r.maxStack { return false, fmt.Errorf("stack overflow") } - stack.Push(NewNumber(num, r.ops.GetMode())) + if r.ops.GetMode() == RationalMode { + rat, err := NewRatFromString(token) + if err != nil { + return false, err + } + stack.Push(rat) + } else { + num, _ := strconv.ParseFloat(token, 64) + stack.Push(NewFloat(num)) + } return true, nil } -- cgit v1.2.3