Skip to content

Match Erlang semantics for div/2 and round/1 - #500

Open
dajiaohuang wants to merge 1 commit into
elixirscript:masterfrom
dajiaohuang:fix/erlang-div-and-debug-log
Open

dajiaohuang wants to merge 1 commit into
elixirscript:masterfrom
dajiaohuang:fix/erlang-div-and-debug-log

Conversation

@dajiaohuang

Copy link
Copy Markdown

Summary

Two functions in the erlang compatibility layer disagree with Erlang on negative operands. Both are reachable from compiled Elixir — div(-7, 2), round(-2.5) and trunc(-2.7) compile to Core.erlang.div, Core.erlang.round and Core.erlang.trunc calls.

div/2 performed float division

function div(left, right) {
  return left / right;
}
Core.erlang.div(-7, 2)  ->  -3.5     Erlang: -3
Core.erlang.div(7, -2)  ->  -3.5     Erlang: -3

Erlang's div truncates toward zero, so Math.trunc(left / right) is the matching implementation. Elixir's div/2 is a guard BIF that lowers to :erlang.div, which is why this is on the compiled path at all.

round/1 rounded half values toward +Infinity

Math.round breaks ties toward +Infinity, but Erlang rounds them away from zero:

Core.erlang.round(-2.5)  ->  -2      Erlang: -3
Core.erlang.round(-0.5)  ->  -0      Erlang: -1

Math.sign(number) * Math.round(Math.abs(number)) rounds half values away from zero as Erlang does.

Stray debug log

nodes/1 printed its accumulator on every call, so any program reaching that branch wrote to stdout as a side effect. Removed.

rem/1 and trunc/1 already match Erlang; the new tests pin that so the four stay consistent as a set.

Changes

  • src/javascript/lib/core/erlang_compat/erlang.js: div/2, round/1, drop console.log in nodes/1
  • src/javascript/tests/core/erlang_compat/erlang_spec.js: tests for div, rem, round, trunc with negative operands

Testing

$ npx ava
  √ core » erlang_compat » erlang_spec » div
  √ core » erlang_compat » erlang_spec » rem
  √ core » erlang_compat » erlang_spec » round
  √ core » erlang_compat » erlang_spec » trunc
  75 tests passed

Each new test was confirmed to fail against the unfixed source.

Caveat on how this was verified: npx ava cannot start on master (see #499), so these runs were made with the changes from #499 applied locally, uncommitted. This PR touches only the runtime file and its spec, and does not depend on #499 to be correct — but its tests cannot run in CI until #499, or an equivalent fix, lands.

Two functions in the erlang compatibility layer diverge from Erlang on
negative operands:

- div/2 used `/`, so div(-7, 2) returned -3.5 instead of -3. Erlang's
  div truncates toward zero.
- round/1 used Math.round, which rounds half values toward +Infinity, so
  round(-2.5) returned -2 instead of -3, and round(-0.5) returned -0
  instead of -1. Erlang rounds half values away from zero.

Both are reachable from compiled Elixir: div(-7, 2) and round(-2.5)
compile to Core.erlang.div and Core.erlang.round calls.

Also drop a leftover console.log in nodes/1 that printed the accumulator
on every call.

Add tests for div, rem, round and trunc covering negative operands.
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