summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-24 10:29:48 +0300
committerPaul Buetow <paul@buetow.org>2026-05-24 10:29:48 +0300
commitc2528014a6c621dca9d46eb70a13db9038e8e5d0 (patch)
tree59a5a3ca3ebaddf0abda12f17d27e39cb97ded4c
parentf8147b44f2888ac150659dced719813f0641fc3c (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.go6
-rw-r--r--internal/rpn/rpn_parse.go15
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
}