X Tutup
Skip to content

fixed == to behave correctly with BigDecimal values and added tests for#21

Closed
ndrchvzz wants to merge 1 commit intoclojure:masterfrom
ndrchvzz:BigDecimal-equiv-fix
Closed

fixed == to behave correctly with BigDecimal values and added tests for#21
ndrchvzz wants to merge 1 commit intoclojure:masterfrom
ndrchvzz:BigDecimal-equiv-fix

Conversation

@ndrchvzz
Copy link
Copy Markdown

The behaviour of == is not correct.

The problem is in this method in Numbers.java:
public boolean equiv(Number x, Number y){
return toBigDecimal(x).equals(toBigDecimal(y));
}

The behaviour we currently have is:
user=> (let [ones [1 1.0 1N 1M 1.0M 1.00M] ](doseq [a ones b ones] %28println a "==" b tab %28== a b) )))
1 == 1 true
1 == 1.0 true
1 == 1N true
1 == 1M true
1 == 1.0M false
1 == 1.00M false
1.0 == 1 true
1.0 == 1.0 true
1.0 == 1N true
1.0 == 1M true
1.0 == 1.0M true
1.0 == 1.00M true
1N == 1 true
1N == 1.0 true
1N == 1N true
1N == 1M true
1N == 1.0M false
1N == 1.00M false
1M == 1 true
1M == 1.0 true
1M == 1N true
1M == 1M true
1M == 1.0M false
1M == 1.00M false
1.0M == 1 false
1.0M == 1.0 true
1.0M == 1N false
1.0M == 1M false
1.0M == 1.0M true
1.0M == 1.00M false
1.00M == 1 false
1.00M == 1.0 true
1.00M == 1N false
1.00M == 1M false
1.00M == 1.0M false
1.00M == 1.00M true

I propose we change the method to be:
public boolean equiv(Number x, Number y){
return toBigDecimal(x).compareTo(toBigDecimal(y)) == 0;
}
This makes the previous expression return all true, and == should also be transitive.

In particular, (== 1.0M 1.00M) will be true rather than false, which is much more in the spirit of ==.
The difference between 1.0M and 1.00M should be checked by calling the scale() method, which makes sense because it is quite an unusual thing to do.

I also noticed in test_clojure/numbers.clj the lines:
; TODO:
; ==
; and more...

So I thought of also adding the test:

(deftest equality
(are [x y](== x y)
42 42
42 42.0
42 42N
42 42M
42 42.0M
42 42.00M
42.0 42
42.0 42.0
42.0 42N
42.0 42M
42.0 42.0M
42.0 42.00M
42N 42
42N 42.0
42N 42N
42N 42M
42N 42.0M
42N 42.00M
42M 42
42M 42.0
42M 42N
42M 42M
42M 42.0M
42M 42.00M
42.0M 42
42.0M 42.0
42.0M 42N
42.0M 42M
42.0M 42.0M
42.0M 42.00M

1.23  1.23
1.23  1.23M
1.23M 1.23
1.23M 1.23M )

(are [x y](not %28== x y%29)
12 12.1
1.23 123
34 3.4
1.23 1.234
123N 345N
123 345N
123N 345
12.34M 456N
12.34M 4.56
12.34 4.56M
12 4.56M
12M 4.56
12.34M 1.234M ))

I think it would be really nice to get this fixed before the 1.4 release.
Can anybody defend the current behaviour against the one I propose ?

cursor bot pushed a commit to The-Alchemist/cloffle-clojure that referenced this pull request Mar 28, 2026
New tests cover:
- Step-into named function reports callee source (clojure#13)
- Step-into multi-arity function (clojure#14)
- Breakpoint on call expression line with StatementTag (clojure#15)
- Three breakpoints fire in order on separate def forms (clojure#16)
- Step-into then step-over stays in callee body (clojure#17)
- Step-into then step-out returns to caller (clojure#18)
- Step-into anonymous fn created with fn form (clojure#19)
- Breakpoint inside multi-line function body (clojure#20)
- Step-into higher-order function call (clojure#21)
- Recursive stack depth monotonically increases (clojure#22)
- Step-out from function returns to caller (clojure#23)
- Breakpoint on if branch fires (clojure#24)
- Breakpoint on let binding fires (clojure#25)
- Step-into closure that captures locals (clojure#26)
- Breakpoint in loop body fires on each iteration (clojure#27)
- Source section at call site has correct characters (clojure#28)
- Breakpoint inside do body (clojure#29)
- Step-into across separate eval contexts (clojure#30)
- Breakpoint with cond macro expansion (clojure#31)
- Multiple step-into follows b->c call chain (clojure#32)

All 737 JUnit tests pass.

Co-authored-by: karl <karl@medplum.com>
The-Alchemist added a commit to The-Alchemist/cloffle-clojure that referenced this pull request Mar 29, 2026
…agation (#6)

* Improve debugger support: RootTag on FnDispatchNode, source section propagation, language reference on FnNode

Key changes:
- FnDispatchNode now reports RootTag alongside RootBodyTag, enabling the
  Truffle debugger to recognize function entry boundaries for step-into
- FnDispatchNode gets its source section from the parent FnNode so that
  isInstrumentable() returns true
- FnNode stores a language reference (set by ExprToNode) and uses it as
  primary source in getCallTarget(), falling back to Clojure.getContext()
  lookup. This ensures per-function ClojureRootNode instances are created
  with a proper language association for stack frame reporting.
- InvokeNode propagates source sections to FnDispatchNode when creating
  static call targets
- truffleEval() now sets source sections and root names on eagerly
  executed form roots
- SequentialFormNode per-form roots now set source sections properly
  (full source first, then narrow when form section is available)
- 8 new debugger tests (20 total, all passing) covering function name
  reporting, multi-line fn breakpoints, step-over, recursive stack
  growth, step-out, source section propagation, and eager eval roots
- All 725 JUnit tests pass

Co-authored-by: karl <karl@medplum.com>

* Update CLOFFLE_NOTES.md with debugger improvements documentation

Document the FnDispatchNode RootTag fix, source section propagation,
language reference on FnNode, truffleEval source sections, and
SequentialFormNode narrowing. Update known limitations to reflect
current state.

Co-authored-by: karl <karl@medplum.com>

* Add StatementTag to hasTag on remaining call node classes

InstanceCallNode, ProtocolInvokeNode, NewNode, NativeCallNode, and
KeywordInvokeNode now report StandardTags.StatementTag alongside CallTag
and ExpressionTag for instrumentation consistency.

Co-authored-by: karl <karl@medplum.com>

* Remove unnecessary @TruffleBoundary, add StatementTag to call nodes, restructure SequentialFormNode

Major debugger improvements:
- SequentialFormNode: Removed @TruffleBoundary from executeSequentially() and
  restructured to use @children DirectCallNode[] created at parse time. Per-form
  roots are now created in the constructor with proper source section narrowing
  (full source first, adoptChildren(), then narrow). This allows the debugger to
  step between top-level forms and enables breakpoints to match nodes inside
  per-form roots.
- InvokeNode, GenericStaticCallNode: Added StatementTag alongside CallTag and
  ExpressionTag. Breakpoints default to matching StatementTag, so call expressions
  were previously invisible to line breakpoints.
- ClojureNode: Removed @TruffleBoundary from getSourceSection() - this is metadata
  computation that doesn't need a compilation boundary.
- ClojureTypes: Removed @TruffleBoundary from castDouble() - trivial cast.
- Updated DebuggerTest: stepIntoFromCallSite now asserts 2 suspensions (step-into
  actually enters the called function body). Updated test comments.
- All 725 JUnit tests pass.

Co-authored-by: karl <karl@medplum.com>

* Update CLOFFLE_NOTES.md: document StatementTag on call nodes, SequentialFormNode restructure, boundary removals

Co-authored-by: karl <karl@medplum.com>

* Add 20 new debugger tests (32 total)

New tests cover:
- Step-into named function reports callee source (clojure#13)
- Step-into multi-arity function (clojure#14)
- Breakpoint on call expression line with StatementTag (clojure#15)
- Three breakpoints fire in order on separate def forms (clojure#16)
- Step-into then step-over stays in callee body (clojure#17)
- Step-into then step-out returns to caller (clojure#18)
- Step-into anonymous fn created with fn form (clojure#19)
- Breakpoint inside multi-line function body (clojure#20)
- Step-into higher-order function call (clojure#21)
- Recursive stack depth monotonically increases (clojure#22)
- Step-out from function returns to caller (clojure#23)
- Breakpoint on if branch fires (clojure#24)
- Breakpoint on let binding fires (clojure#25)
- Step-into closure that captures locals (clojure#26)
- Breakpoint in loop body fires on each iteration (clojure#27)
- Source section at call site has correct characters (clojure#28)
- Breakpoint inside do body (clojure#29)
- Step-into across separate eval contexts (clojure#30)
- Breakpoint with cond macro expansion (clojure#31)
- Multiple step-into follows b->c call chain (clojure#32)

All 737 JUnit tests pass.

Co-authored-by: karl <karl@medplum.com>

* Add 20 more debugger tests (52 total)

New tests cover:
- Step-over does not enter callee (clojure#33)
- Breakpoint inside try body (clojure#34)
- Step-into variadic function (clojure#35)
- Breakpoint on case form (clojure#36)
- Breakpoint on throw form (clojure#37)
- Breakpoint on recur form fires each iteration (clojure#38)
- Source file name at breakpoint is correct (clojure#39)
- Step-into locally-defined function (let + fn) (clojure#40)
- Breakpoint on nested let (clojure#41)
- Breakpoint removal prevents further hits (clojure#42)
- Breakpoint on keyword invoke (:key map) (clojure#43)
- Breakpoint inside letfn with mutual recursion (clojure#44)
- Breakpoint on Java interop .method call (clojure#45)
- Breakpoint on constructor call (clojure#46)
- Breakpoint on static method call (clojure#47)
- Step-into multi-arity with delegating arity (clojure#48)
- Continue after multiple breakpoints resumes fully (clojure#49)
- Breakpoint on and/or macro expansion (clojure#50)
- Source section length matches form length (clojure#51)
- Breakpoint on when macro (clojure#52)

All 757 JUnit tests pass.

Co-authored-by: karl <karl@medplum.com>

* Add 18 advanced debugger tests (70 total)

Advanced debugger feature tests:
- Conditional breakpoint fires on loop iterations (clojure#53)
- One-shot breakpoint fires exactly once (clojure#54)
- Breakpoint ignoreCount skips initial hits (clojure#55)
- Breakpoint hit count tracks total activations (clojure#56)
- DebugStackFrame.getScope() returns local variable scope (clojure#57)
- DebugStackFrame.getLanguage() returns cloffle language info (clojure#58)
- Internal frames not visible in default mode (clojure#59)
- SuspendedEvent.getSuspendAnchor() returns BEFORE (clojure#60)
- Return value available after step-over (clojure#61)
- Breakpoint isResolved after execution (clojure#62)
- Breakpoint enable/disable toggle (clojure#63)
- SuspendedEvent.getBreakpoints() reports the firing breakpoint (clojure#64)
- Step-into count > 1 steps multiple times (clojure#65)
- Step-over count > 1 skips multiple statements (clojure#66)
- isBreakpointHit() vs isStep() distinction (clojure#67)
- Breakpoint on function in different source fires on call (clojure#68)
- Breakpoint on defn line fires during definition (clojure#69)
- Eval expression in suspended frame context (clojure#70)

All 775 JUnit tests pass.

Co-authored-by: karl <karl@medplum.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

X Tutup