Skip to content


Browse files Browse the repository at this point in the history
[CSS Math Functions] Correct mod() evaluation

Reviewed by Simon Fraser.

According to :

Their behavior diverges if the A value and the B step are on opposite sides of zero: mod() (short
for “modulus”) continues to choose the integer multiple of B that puts the value between zero and
B, as above (guaranteeing that the result will either be zero or share the sign of B, not A), while
rem() (short for "remainder") chooses the integer multiple of B that puts the value between zero
and -B, avoiding changing the sign of the value.

* LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt:
* Source/WebCore/platform/calc/CalcOperator.h:

Canonical link:
  • Loading branch information
nt1m committed Aug 1, 2023
1 parent 48aceba commit 440d1ba
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 5 deletions.
Expand Up @@ -26,7 +26,7 @@ PASS calc(mod(18,5) * 2 + mod(17,5)) should be used-value-equivalent to 8
PASS calc(rem(mod(18,5),5)) should be used-value-equivalent to 3
PASS calc(rem(mod(18,5),mod(17,5))) should be used-value-equivalent to 1
PASS calc(mod(-140,-90)) should be used-value-equivalent to -50
FAIL calc(mod(rem(1,18)* -1,5)) should be used-value-equivalent to 4 assert_equals: calc(mod(rem(1,18)* -1,5)) and 4 serialize to the same thing in used values. expected "matrix(4, 0, 0, 4, 0, 0)" but got "matrix(-1, 0, 0, -1, 0, 0)"
PASS calc(mod(rem(1,18)* -1,5)) should be used-value-equivalent to 4

This comment has been minimized.

Copy link

darinadler Aug 2, 2023


I’m a bit surprised that there are two code changes, but only one test case affected.

This comment has been minimized.

Copy link

nt1m Aug 2, 2023

Author Member
PASS round(10px,6px) should be used-value-equivalent to 12px
PASS round(10cm,6cm) should be used-value-equivalent to 12cm
PASS round(10mm,6mm) should be used-value-equivalent to 12mm
Expand Down
15 changes: 11 additions & 4 deletions Source/WebCore/platform/calc/CalcOperator.h
Expand Up @@ -225,11 +225,18 @@ double evaluateCalcExpression(CalcOperator calcOperator, const Vector<T>& childr
return std::numeric_limits<double>::quiet_NaN();
auto left = evaluate(children[0]);
auto right = evaluate(children[1]);
if (!right)
// In mod(A, B) only, if B is infinite and A has opposite sign to B
// (including an oppositely-signed zero), the result is NaN.
if (std::isinf(right) && std::signbit(left) != std::signbit(right))
return std::numeric_limits<double>::quiet_NaN();
if ((left < 0) == (right < 0))
return std::fmod(left, right);
return std::remainder(left, right);
auto result = std::fmod(left, right);
// If the result is on opposite side of zero from B,
// put it between 0 and B.
if (std::signbit(result) != std::signbit(right))
result += right;
return result;
case CalcOperator::Rem: {
if (children.size() != 2)
Expand Down

0 comments on commit 440d1ba

Please sign in to comment.