/* * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Some arithmetic functions that seem to pop up or get hand-rolled a lot. * So far they are all focused on integer division. */ #pragma once #include #include #include namespace folly { namespace detail { template inline constexpr T divFloorBranchless(T num, T denom) { // floor != trunc when the answer isn't exact and truncation went the // wrong way (truncation went toward positive infinity). That happens // when the true answer is negative, which happens when num and denom // have different signs. The following code compiles branch-free on // many platforms. return (num / denom) + ((num % denom) != 0 ? 1 : 0) * (std::is_signed::value && (num ^ denom) < 0 ? -1 : 0); } template inline constexpr T divFloorBranchful(T num, T denom) { // First case handles negative result by preconditioning numerator. // Preconditioning decreases the magnitude of the numerator, which is // itself sign-dependent. Second case handles zero or positive rational // result, where trunc and floor are the same. return std::is_signed::value && (num ^ denom) < 0 && num != 0 ? (num + (num > 0 ? -1 : 1)) / denom - 1 : num / denom; } template inline constexpr T divCeilBranchless(T num, T denom) { // ceil != trunc when the answer isn't exact (truncation occurred) // and truncation went away from positive infinity. That happens when // the true answer is positive, which happens when num and denom have // the same sign. return (num / denom) + ((num % denom) != 0 ? 1 : 0) * (std::is_signed::value && (num ^ denom) < 0 ? 0 : 1); } template inline constexpr T divCeilBranchful(T num, T denom) { // First case handles negative or zero rational result, where trunc and ceil // are the same. // Second case handles positive result by preconditioning numerator. // Preconditioning decreases the magnitude of the numerator, which is // itself sign-dependent. return (std::is_signed::value && (num ^ denom) < 0) || num == 0 ? num / denom : (num + (num > 0 ? -1 : 1)) / denom + 1; } template inline constexpr T divRoundAwayBranchless(T num, T denom) { // away != trunc whenever truncation actually occurred, which is when // there is a non-zero remainder. If the unrounded result is negative // then fixup moves it toward negative infinity. If the unrounded // result is positive then adjustment makes it larger. return (num / denom) + ((num % denom) != 0 ? 1 : 0) * (std::is_signed::value && (num ^ denom) < 0 ? -1 : 1); } template inline constexpr T divRoundAwayBranchful(T num, T denom) { // First case of second ternary operator handles negative rational // result, which is the same as divFloor. Second case of second ternary // operator handles positive result, which is the same as divCeil. // Zero case is separated for simplicity. return num == 0 ? 0 : (num + (num > 0 ? -1 : 1)) / denom + (std::is_signed::value && (num ^ denom) < 0 ? -1 : 1); } template using IdivResultType = typename std::enable_if< std::is_integral::value && std::is_integral::value && !std::is_same::value && !std::is_same::value, decltype(N{1} / D{1})>::type; } // namespace detail #if defined(__arm__) && !FOLLY_AARCH64 constexpr auto kIntegerDivisionGivesRemainder = false; #else constexpr auto kIntegerDivisionGivesRemainder = true; #endif /** * Returns num/denom, rounded toward negative infinity. Put another way, * returns the largest integral value that is less than or equal to the * exact (not rounded) fraction num/denom. * * The matching remainder (num - divFloor(num, denom) * denom) can be * negative only if denom is negative, unlike in truncating division. * Note that for unsigned types this is the same as the normal integer * division operator. divFloor is equivalent to python's integral division * operator //. * * This function undergoes the same integer promotion rules as a * built-in operator, except that we don't allow bool -> int promotion. * This function is undefined if denom == 0. It is also undefined if the * result type T is a signed type, num is std::numeric_limits::min(), * and denom is equal to -1 after conversion to the result type. */ template inline constexpr detail::IdivResultType divFloor(N num, D denom) { using R = decltype(num / denom); return detail::IdivResultType( kIntegerDivisionGivesRemainder && std::is_signed::value ? detail::divFloorBranchless(num, denom) : detail::divFloorBranchful(num, denom)); } /** * Returns num/denom, rounded toward positive infinity. Put another way, * returns the smallest integral value that is greater than or equal to * the exact (not rounded) fraction num/denom. * * This function undergoes the same integer promotion rules as a * built-in operator, except that we don't allow bool -> int promotion. * This function is undefined if denom == 0. It is also undefined if the * result type T is a signed type, num is std::numeric_limits::min(), * and denom is equal to -1 after conversion to the result type. */ template inline constexpr detail::IdivResultType divCeil(N num, D denom) { using R = decltype(num / denom); return detail::IdivResultType( kIntegerDivisionGivesRemainder && std::is_signed::value ? detail::divCeilBranchless(num, denom) : detail::divCeilBranchful(num, denom)); } /** * Returns num/denom, rounded toward zero. If num and denom are non-zero * and have different signs (so the unrounded fraction num/denom is * negative), returns divCeil, otherwise returns divFloor. If T is an * unsigned type then this is always equal to divFloor. * * Note that this is the same as the normal integer division operator, * at least since C99 (before then the rounding for negative results was * implementation defined). This function is here for completeness and * as a place to hang this comment. * * This function undergoes the same integer promotion rules as a * built-in operator, except that we don't allow bool -> int promotion. * This function is undefined if denom == 0. It is also undefined if the * result type T is a signed type, num is std::numeric_limits::min(), * and denom is equal to -1 after conversion to the result type. */ template inline constexpr detail::IdivResultType divTrunc(N num, D denom) { return detail::IdivResultType(num / denom); } /** * Returns num/denom, rounded away from zero. If num and denom are * non-zero and have different signs (so the unrounded fraction num/denom * is negative), returns divFloor, otherwise returns divCeil. If T is * an unsigned type then this is always equal to divCeil. * * This function undergoes the same integer promotion rules as a * built-in operator, except that we don't allow bool -> int promotion. * This function is undefined if denom == 0. It is also undefined if the * result type T is a signed type, num is std::numeric_limits::min(), * and denom is equal to -1 after conversion to the result type. */ template inline constexpr detail::IdivResultType divRoundAway(N num, D denom) { using R = decltype(num / denom); return detail::IdivResultType( kIntegerDivisionGivesRemainder && std::is_signed::value ? detail::divRoundAwayBranchless(num, denom) : detail::divRoundAwayBranchful(num, denom)); } } // namespace folly