diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-24 10:29:48 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-24 10:29:48 +0300 |
| commit | c2528014a6c621dca9d46eb70a13db9038e8e5d0 (patch) | |
| tree | 59a5a3ca3ebaddf0abda12f17d27e39cb97ded4c | |
| parent | f8147b44f2888ac150659dced719813f0641fc3c (diff) | |
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.
| -rw-r--r-- | internal/rpn/rpn_ops.go | 6 | ||||
| -rw-r--r-- | 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 } |
