From 4ea2d50a5f28aa5b54ae68064d9421f3cc0b1060 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Sat, 8 Jul 2023 23:39:53 -0700 Subject: [PATCH] [CSS Math Functions] De-duplicate `CSSCalcOperationNode::evaluateOperator` & `CalcExpressionOperation::evaluate` https://bugs.webkit.org/show_bug.cgi?id=259021 rdar://111962034 Reviewed by Darin Adler. Use a common `evaluateCalcExpression` template function. * Source/WebCore/css/calc/CSSCalcOperationNode.cpp: (WebCore::CSSCalcOperationNode::evaluateOperator): (WebCore::getNearestMultiples): Deleted. * Source/WebCore/platform/calc/CalcExpressionOperation.cpp: (WebCore::CalcExpressionOperation::evaluate const): (WebCore::getNearestMultiples): Deleted. * Source/WebCore/platform/calc/CalcOperator.h: (WebCore::evaluateCalcExpression): Canonical link: https://commits.webkit.org/265888@main --- .../WebCore/css/calc/CSSCalcOperationNode.cpp | 227 +---------------- .../platform/calc/CalcExpressionOperation.cpp | 217 +---------------- Source/WebCore/platform/calc/CalcOperator.h | 229 ++++++++++++++++++ 3 files changed, 236 insertions(+), 437 deletions(-) diff --git a/Source/WebCore/css/calc/CSSCalcOperationNode.cpp b/Source/WebCore/css/calc/CSSCalcOperationNode.cpp index ee230cafb019..2807721e7def 100644 --- a/Source/WebCore/css/calc/CSSCalcOperationNode.cpp +++ b/Source/WebCore/css/calc/CSSCalcOperationNode.cpp @@ -37,7 +37,6 @@ #include "Logging.h" #include #include -#include #include namespace WebCore { @@ -1273,229 +1272,11 @@ bool CSSCalcOperationNode::equals(const CSSCalcExpressionNode& exp) const return true; } -static std::pair getNearestMultiples(double a, double b) +double CSSCalcOperationNode::evaluateOperator(CalcOperator calcOperator, const Vector& children) { - double lowerB = std::floor(a / std::abs(b))*std::abs(b); - double upperB = lowerB + std::abs(b); - return std::make_pair(lowerB, upperB); -} - -double CSSCalcOperationNode::evaluateOperator(CalcOperator op, const Vector& children) -{ - switch (op) { - case CalcOperator::Add: { - double sum = 0; - for (auto& child : children) - sum += child; - return sum; - } - case CalcOperator::Subtract: - ASSERT(children.size() == 2); - return children[0] - children[1]; - case CalcOperator::Multiply: { - double product = 1; - for (auto& child : children) - product *= child; - return product; - } - case CalcOperator::Divide: - ASSERT(children.size() == 1 || children.size() == 2); - if (children.size() == 1) - return std::numeric_limits::quiet_NaN(); - return children[0] / children[1]; - case CalcOperator::Min: { - if (children.isEmpty()) - return std::numeric_limits::quiet_NaN(); - double minimum = children[0]; - for (auto child : children) { - if (std::isnan(child)) - return child; - minimum = std::min(minimum, child); - } - return minimum; - } - case CalcOperator::Max: { - if (children.isEmpty()) - return std::numeric_limits::quiet_NaN(); - double maximum = children[0]; - for (auto child : children) { - if (std::isnan(child)) - return child; - maximum = std::max(maximum, child); - } - return maximum; - } - case CalcOperator::Clamp: { - if (children.size() != 3) - return std::numeric_limits::quiet_NaN(); - double min = children[0]; - double value = children[1]; - double max = children[2]; - if (std::isnan(min) || std::isnan(value) || std::isnan(max)) - return std::numeric_limits::quiet_NaN(); - return std::max(min, std::min(value, max)); - } - case CalcOperator::Pow: - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - return std::pow(children[0], children[1]); - case CalcOperator::Sqrt: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::sqrt(children[0]); - } - case CalcOperator::Hypot: { - if (children.isEmpty()) - return std::numeric_limits::quiet_NaN(); - if (children.size() == 1) - return std::abs(children[0]); - double sum = 0; - for (auto child : children) { - if (std::isnan(child)) - return child; - sum += (child * child); - } - return std::sqrt(sum); - } - case CalcOperator::Sin: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::sin(children[0]); - } - case CalcOperator::Cos: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::cos(children[0]); - } - case CalcOperator::Tan: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - double x = std::fmod(children[0], piDouble * 2); - // std::fmod can return negative values. - x = x < 0 ? piDouble * 2 + x : x; - ASSERT(!(x < 0)); - ASSERT(!(x > piDouble * 2)); - if (x == piOverTwoDouble) - return std::numeric_limits::infinity(); - if (x == 3 * piOverTwoDouble) - return -std::numeric_limits::infinity(); - return std::tan(x); - } - case CalcOperator::Log: { - if (children.size() != 1 && children.size() != 2) - return std::numeric_limits::quiet_NaN(); - if (children.size() == 1) - return std::log(children[0]); - return std::log(children[0]) / std::log(children[1]); - } - case CalcOperator::Exp: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::exp(children[0]); - } - case CalcOperator::Asin: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return rad2deg(std::asin(children[0])); - } - case CalcOperator::Acos: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return rad2deg(std::acos(children[0])); - } - case CalcOperator::Atan: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return rad2deg(std::atan(children[0])); - } - case CalcOperator::Atan2: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - return rad2deg(atan2(children[0], children[1])); - } - case CalcOperator::Abs: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::abs(children[0]); - } - case CalcOperator::Sign: { - if (children.size() != 1) - return std::numeric_limits::quiet_NaN(); - if (children[0] > 0) - return 1; - if (children[0] < 0) - return -1; - return children[0]; - } - case CalcOperator::Mod: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - float left = children[0]; - float right = children[1]; - if (!right) - return std::numeric_limits::quiet_NaN(); - if ((left < 0) == (right < 0)) - return std::fmod(left, right); - return std::remainder(left, right); - } - case CalcOperator::Rem: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - float left = children[0]; - float right = children[1]; - if (!right) - return std::numeric_limits::quiet_NaN(); - return std::fmod(left, right); - } - case CalcOperator::Round: - return std::numeric_limits::quiet_NaN(); - case CalcOperator::Up: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - if (!isinf(children[0]) && std::isinf(children[1])) { - if (!children[0]) - return children[0]; - return signbit(children[0]) ? -0.0 : std::numeric_limits::infinity(); - } - auto ret = getNearestMultiples(children[0], children[1]); - return ret.second; - } - case CalcOperator::Down: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - if (!isinf(children[0]) && isinf(children[1])) { - if (!children[0]) - return children[0]; - return signbit(children[0]) ? -std::numeric_limits::infinity() : +0.0; - } - auto ret = getNearestMultiples(children[0], children[1]); - return ret.first; - } - case CalcOperator::Nearest: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - if (!isinf(children[0]) && isinf(children[1])) - return signbit(children[0]) ? -0.0 : +0.0; - auto ret = getNearestMultiples(children[0], children[1]); - auto upperB = ret.second; - auto lowerB = ret.first; - return std::abs(upperB - children[0]) <= std::abs(children[1]) / 2 ? upperB : lowerB; - } - case CalcOperator::ToZero: { - if (children.size() != 2) - return std::numeric_limits::quiet_NaN(); - if (!isinf(children[0]) && isinf(children[1])) - return signbit(children[0]) ? -0.0 : +0.0; - auto ret = getNearestMultiples(children[0], children[1]); - auto upperB = ret.second; - auto lowerB = ret.first; - return std::abs(upperB) < std::abs(lowerB) ? upperB : lowerB; - } - } - ASSERT_NOT_REACHED(); - return 0; + return evaluateCalcExpression(calcOperator, children, [](double child) { + return child; + }); } - - } diff --git a/Source/WebCore/platform/calc/CalcExpressionOperation.cpp b/Source/WebCore/platform/calc/CalcExpressionOperation.cpp index f7b0dde4879c..29067911341c 100644 --- a/Source/WebCore/platform/calc/CalcExpressionOperation.cpp +++ b/Source/WebCore/platform/calc/CalcExpressionOperation.cpp @@ -25,226 +25,15 @@ #include "config.h" #include "CalcExpressionOperation.h" - -#include -#include #include namespace WebCore { -static std::pair getNearestMultiples(double a, double b) -{ - auto absB = std::abs(b); - double lowerB = std::floor(a / absB) * absB; - double upperB = lowerB + absB; - return std::make_pair(lowerB, upperB); -} - float CalcExpressionOperation::evaluate(float maxValue) const { - switch (m_operator) { - case CalcOperator::Add: { - float sum = 0; - for (auto& child : m_children) - sum += child->evaluate(maxValue); - return sum; - } - case CalcOperator::Subtract: { - // FIXME - ASSERT(m_children.size() == 2); - float left = m_children[0]->evaluate(maxValue); - float right = m_children[1]->evaluate(maxValue); - return left - right; - } - case CalcOperator::Multiply: { - float product = 1; - for (auto& child : m_children) - product *= child->evaluate(maxValue); - return product; - } - case CalcOperator::Divide: { - // FIXME - ASSERT(m_children.size() == 1 || m_children.size() == 2); - if (m_children.size() == 1) - return std::numeric_limits::quiet_NaN(); - float left = m_children[0]->evaluate(maxValue); - float right = m_children[1]->evaluate(maxValue); - return left / right; - } - case CalcOperator::Min: { - if (m_children.isEmpty()) - return std::numeric_limits::quiet_NaN(); - float minimum = m_children[0]->evaluate(maxValue); - for (auto& child : m_children) - minimum = std::min(minimum, child->evaluate(maxValue)); - return minimum; - } - case CalcOperator::Max: { - if (m_children.isEmpty()) - return std::numeric_limits::quiet_NaN(); - float maximum = m_children[0]->evaluate(maxValue); - for (auto& child : m_children) - maximum = std::max(maximum, child->evaluate(maxValue)); - return maximum; - } - case CalcOperator::Clamp: { - if (m_children.size() != 3) - return std::numeric_limits::quiet_NaN(); - - float min = m_children[0]->evaluate(maxValue); - float value = m_children[1]->evaluate(maxValue); - float max = m_children[2]->evaluate(maxValue); - return std::max(min, std::min(value, max)); - } - case CalcOperator::Pow: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - float base = m_children[0]->evaluate(maxValue); - float power = m_children[1]->evaluate(maxValue); - return std::pow(base, power); - } - case CalcOperator::Sqrt: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::sqrt(m_children[0]->evaluate(maxValue)); - } - case CalcOperator::Hypot: { - if (m_children.isEmpty()) - return std::numeric_limits::quiet_NaN(); - if (m_children.size() == 1) - return std::abs(m_children[0]->evaluate(maxValue)); - float sum = 0; - for (auto& child : m_children) { - float value = child->evaluate(maxValue); - sum += (value * value); - } - return std::sqrt(sum); - } - case CalcOperator::Sin: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::sin(m_children[0]->evaluate(maxValue)); - } - case CalcOperator::Cos: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::cos(m_children[0]->evaluate(maxValue)); - } - case CalcOperator::Tan: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - double x = std::fmod(m_children[0]->evaluate(maxValue), piDouble * 2); - // std::fmod can return negative values. - x = x < 0 ? piDouble * 2 + x : x; - ASSERT(!(x < 0)); - ASSERT(!(x > piDouble * 2)); - if (x == piOverTwoDouble) - return std::numeric_limits::infinity(); - if (x == 3 * piOverTwoDouble) - return -std::numeric_limits::infinity(); - return std::tan(x); - } - case CalcOperator::Log: { - if (m_children.size() != 1 && m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - if (m_children.size() == 1) - return std::log(m_children[0]->evaluate(maxValue)); - return std::log(m_children[0]->evaluate(maxValue)) / std::log(m_children[1]->evaluate(maxValue)); - } - case CalcOperator::Exp: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::exp(m_children[0]->evaluate(maxValue)); - } - case CalcOperator::Asin: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return rad2deg(std::asin(m_children[0]->evaluate(maxValue))); - } - case CalcOperator::Acos: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return rad2deg(std::acos(m_children[0]->evaluate(maxValue))); - } - case CalcOperator::Atan: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return rad2deg(std::atan(m_children[0]->evaluate(maxValue))); - } - case CalcOperator::Atan2: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - return rad2deg(atan2(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue))); - } - case CalcOperator::Abs: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - return std::abs(m_children[0]->evaluate(maxValue)); - } - case CalcOperator::Sign: { - if (m_children.size() != 1) - return std::numeric_limits::quiet_NaN(); - if (m_children[0]->evaluate(maxValue) > 0) - return 1; - if (m_children[0]->evaluate(maxValue) < 0) - return -1; - return m_children[0]->evaluate(maxValue); - } - case CalcOperator::Mod: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - float left = m_children[0]->evaluate(maxValue); - float right = m_children[1]->evaluate(maxValue); - if (!right) - return std::numeric_limits::quiet_NaN(); - if ((left < 0) == (right < 0)) - return std::fmod(left, right); - return std::remainder(left, right); - } - case CalcOperator::Rem: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - float left = m_children[0]->evaluate(maxValue); - float right = m_children[1]->evaluate(maxValue); - if (!right) - return std::numeric_limits::quiet_NaN(); - return std::fmod(left, right); - } - case CalcOperator::Round: - return std::numeric_limits::quiet_NaN(); - case CalcOperator::Up: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - auto ret = getNearestMultiples(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue)); - return ret.second; - } - case CalcOperator::Down: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - auto ret = getNearestMultiples(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue)); - return ret.first; - } - case CalcOperator::Nearest: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - auto a = m_children[0]->evaluate(maxValue); - auto b = m_children[1]->evaluate(maxValue); - auto ret = getNearestMultiples(a, b); - auto upperB = ret.second; - auto lowerB = ret.first; - return std::abs(upperB - a) <= std::abs(b) / 2 ? upperB : lowerB; - } - case CalcOperator::ToZero: { - if (m_children.size() != 2) - return std::numeric_limits::quiet_NaN(); - auto ret = getNearestMultiples(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue)); - auto upperB = ret.second; - auto lowerB = ret.first; - return std::abs(upperB) < std::abs(lowerB) ? upperB : lowerB; - } - } - ASSERT_NOT_REACHED(); - return std::numeric_limits::quiet_NaN(); + return evaluateCalcExpression(m_operator, m_children, [&](auto& child) { + return child->evaluate(maxValue); + }); } bool CalcExpressionOperation::operator==(const CalcExpressionNode& other) const diff --git a/Source/WebCore/platform/calc/CalcOperator.h b/Source/WebCore/platform/calc/CalcOperator.h index 60288d136665..7f005418c9dc 100644 --- a/Source/WebCore/platform/calc/CalcOperator.h +++ b/Source/WebCore/platform/calc/CalcOperator.h @@ -25,7 +25,9 @@ #pragma once +#include #include +#include namespace WebCore { @@ -63,4 +65,231 @@ enum class CalcOperator : uint8_t { TextStream& operator<<(TextStream&, CalcOperator); +template +double evaluateCalcExpression(CalcOperator calcOperator, const Vector& children, Function&& evaluate) +{ + auto getNearestMultiples = [](double a, double b) -> std::pair { + double lower = std::floor(a / std::abs(b)) * std::abs(b); + double upper = lower + std::abs(b); + return { lower, upper }; + }; + + switch (calcOperator) { + case CalcOperator::Add: { + double sum = 0; + for (auto& child : children) + sum += evaluate(child); + return sum; + } + case CalcOperator::Subtract: + ASSERT(children.size() == 2); + return evaluate(children[0]) - evaluate(children[1]); + case CalcOperator::Multiply: { + double product = 1; + for (auto& child : children) + product *= evaluate(child); + return product; + } + case CalcOperator::Divide: + ASSERT(children.size() == 1 || children.size() == 2); + if (children.size() == 1) + return std::numeric_limits::quiet_NaN(); + return evaluate(children[0]) / evaluate(children[1]); + case CalcOperator::Min: { + if (children.isEmpty()) + return std::numeric_limits::quiet_NaN(); + auto minimum = evaluate(children[0]); + for (auto& child : children) { + auto value = evaluate(child); + if (std::isnan(value)) + return value; + minimum = std::min(minimum, value); + } + return minimum; + } + case CalcOperator::Max: { + if (children.isEmpty()) + return std::numeric_limits::quiet_NaN(); + auto maximum = evaluate(children[0]); + for (auto& child : children) { + auto value = evaluate(child); + if (std::isnan(value)) + return value; + maximum = std::max(maximum, value); + } + return maximum; + } + case CalcOperator::Clamp: { + if (children.size() != 3) + return std::numeric_limits::quiet_NaN(); + double min = evaluate(children[0]); + double value = evaluate(children[1]); + double max = evaluate(children[2]); + if (std::isnan(min) || std::isnan(value) || std::isnan(max)) + return std::numeric_limits::quiet_NaN(); + return std::max(min, std::min(value, max)); + } + case CalcOperator::Pow: + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + return std::pow(evaluate(children[0]), evaluate(children[1])); + case CalcOperator::Sqrt: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return std::sqrt(evaluate(children[0])); + } + case CalcOperator::Hypot: { + if (children.isEmpty()) + return std::numeric_limits::quiet_NaN(); + if (children.size() == 1) + return std::abs(evaluate(children[0])); + double sum = 0; + for (auto& child : children) { + auto value = evaluate(child); + sum += (value * value); + } + return std::sqrt(sum); + } + case CalcOperator::Sin: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return std::sin(evaluate(children[0])); + } + case CalcOperator::Cos: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return std::cos(evaluate(children[0])); + } + case CalcOperator::Tan: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + double x = std::fmod(evaluate(children[0]), piDouble * 2); + // std::fmod can return negative values. + x = x < 0 ? piDouble * 2 + x : x; + ASSERT(!(x < 0)); + ASSERT(!(x > piDouble * 2)); + if (x == piOverTwoDouble) + return std::numeric_limits::infinity(); + if (x == 3 * piOverTwoDouble) + return -std::numeric_limits::infinity(); + return std::tan(x); + } + case CalcOperator::Log: { + if (children.size() != 1 && children.size() != 2) + return std::numeric_limits::quiet_NaN(); + if (children.size() == 1) + return std::log(evaluate(children[0])); + return std::log(evaluate(children[0])) / std::log(evaluate(children[1])); + } + case CalcOperator::Exp: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return std::exp(evaluate(children[0])); + } + case CalcOperator::Asin: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return rad2deg(std::asin(evaluate(children[0]))); + } + case CalcOperator::Acos: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return rad2deg(std::acos(evaluate(children[0]))); + } + case CalcOperator::Atan: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return rad2deg(std::atan(evaluate(children[0]))); + } + case CalcOperator::Atan2: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + return rad2deg(atan2(evaluate(children[0]), evaluate(children[1]))); + } + case CalcOperator::Abs: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + return std::abs(evaluate(children[0])); + } + case CalcOperator::Sign: { + if (children.size() != 1) + return std::numeric_limits::quiet_NaN(); + auto value = evaluate(children[0]); + if (value > 0) + return 1; + if (value < 0) + return -1; + return value; + } + case CalcOperator::Mod: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + auto left = evaluate(children[0]); + auto right = evaluate(children[1]); + if (!right) + return std::numeric_limits::quiet_NaN(); + if ((left < 0) == (right < 0)) + return std::fmod(left, right); + return std::remainder(left, right); + } + case CalcOperator::Rem: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + auto left = evaluate(children[0]); + auto right = evaluate(children[1]); + if (!right) + return std::numeric_limits::quiet_NaN(); + return std::fmod(left, right); + } + case CalcOperator::Round: + return std::numeric_limits::quiet_NaN(); + case CalcOperator::Up: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + auto valueToRound = evaluate(children[0]); + auto roundingInterval = evaluate(children[1]); + if (!std::isinf(valueToRound) && std::isinf(roundingInterval)) { + if (!valueToRound) + return valueToRound; + return std::signbit(valueToRound) ? -0.0 : std::numeric_limits::infinity(); + } + return getNearestMultiples(valueToRound, roundingInterval).second; + } + case CalcOperator::Down: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + auto valueToRound = evaluate(children[0]); + auto roundingInterval = evaluate(children[1]); + if (!std::isinf(valueToRound) && std::isinf(roundingInterval)) { + if (!valueToRound) + return valueToRound; + return std::signbit(valueToRound) ? -std::numeric_limits::infinity() : +0.0; + } + return getNearestMultiples(valueToRound, roundingInterval).first; + } + case CalcOperator::Nearest: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + auto valueToRound = evaluate(children[0]); + auto roundingInterval = evaluate(children[1]); + if (!std::isinf(valueToRound) && std::isinf(roundingInterval)) + return std::signbit(valueToRound) ? -0.0 : +0.0; + auto [lower, upper] = getNearestMultiples(valueToRound, roundingInterval); + return std::abs(upper - valueToRound) <= std::abs(roundingInterval) / 2 ? upper : lower; + } + case CalcOperator::ToZero: { + if (children.size() != 2) + return std::numeric_limits::quiet_NaN(); + auto valueToRound = evaluate(children[0]); + auto roundingInterval = evaluate(children[1]); + if (!std::isinf(valueToRound) && std::isinf(roundingInterval)) + return std::signbit(valueToRound) ? -0.0 : +0.0; + auto [lower, upper] = getNearestMultiples(valueToRound, roundingInterval); + return std::abs(upper) < std::abs(lower) ? upper : lower; + } + } + ASSERT_NOT_REACHED(); + return 0; +} + }