// Copyright 2023 PingCAP, 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. #pragma once #include #include #include #include #include #include /** Preceptually-correct number comparisons. * Example: Int8(-1) != UInt8(255) */ namespace accurate { template bool lessOp(A a, B b) { if constexpr (std::is_same_v) return a < b; /// float vs float if constexpr (std::is_floating_point_v && std::is_floating_point_v) return a < b; /// anything vs NaN if (isNaN(a) || isNaN(b)) return false; /// int vs int (including boost int) if constexpr (is_integer_v && is_integer_v) { /// same signedness if constexpr (is_signed_v == is_signed_v) return a < b; /// different signedness if constexpr (is_signed_v && !is_signed_v) return a < 0 || static_cast>(a) < b; if constexpr (!is_signed_v && is_signed_v) return b >= 0 && a < static_cast>(b); } /// int (including boost int) vs float if constexpr (is_integer_v && std::is_floating_point_v) { if constexpr (std::numeric_limits::digits <= 32) return static_cast(a) < static_cast(b); return DecomposedFloat(b).greater(a); } if constexpr (std::is_floating_point_v && is_integer_v) { if constexpr (std::numeric_limits::digits <= 32) return static_cast(a) < static_cast(b); return DecomposedFloat(a).less(b); } static_assert(is_integer_v || std::is_floating_point_v); static_assert(is_integer_v || std::is_floating_point_v); __builtin_unreachable(); } template bool greaterOp(A a, B b) { return lessOp(b, a); } template bool greaterOrEqualsOp(A a, B b) { if (isNaN(a) || isNaN(b)) return false; return !lessOp(a, b); } template bool lessOrEqualsOp(A a, B b) { if (isNaN(a) || isNaN(b)) return false; return !lessOp(b, a); } template bool equalsOp(A a, B b) { if constexpr (std::is_same_v) return a == b; /// float vs float if constexpr (std::is_floating_point_v && std::is_floating_point_v) return a == b; /// anything vs NaN if (isNaN(a) || isNaN(b)) return false; /// int vs int (including boost int) if constexpr (is_integer_v && is_integer_v) { /// same signedness if constexpr (is_signed_v == is_signed_v) return a == b; /// different signedness if constexpr (is_signed_v && !is_signed_v) return a >= 0 && static_cast>(a) == b; if constexpr (!is_signed_v && is_signed_v) return b >= 0 && a == static_cast>(b); } /// int (including boost int) vs float if constexpr (is_integer_v && std::is_floating_point_v) { if constexpr (sizeof(A) <= 4) return static_cast(a) == static_cast(b); return DecomposedFloat(b).equals(a); } if constexpr (std::is_floating_point_v && is_integer_v) { if constexpr (sizeof(B) <= 4) return static_cast(a) == static_cast(b); return DecomposedFloat(a).equals(b); } /// e.g comparing String with integer. return false; } template bool notEqualsOp(A a, B b) { return !equalsOp(a, b); } } // namespace accurate namespace DB { template struct EqualsOp { /// An operation that gives the same result, if arguments are passed in reverse order. using SymmetricOp = EqualsOp; static UInt8 apply(A a, B b) { return accurate::equalsOp(a, b); } }; template struct NotEqualsOp { using SymmetricOp = NotEqualsOp; static UInt8 apply(A a, B b) { return accurate::notEqualsOp(a, b); } }; template struct GreaterOp; template struct LessOp { using SymmetricOp = GreaterOp; static UInt8 apply(A a, B b) { return accurate::lessOp(a, b); } }; template struct GreaterOp { using SymmetricOp = LessOp; static UInt8 apply(A a, B b) { return accurate::greaterOp(a, b); } }; template struct GreaterOrEqualsOp; template struct LessOrEqualsOp { using SymmetricOp = GreaterOrEqualsOp; static UInt8 apply(A a, B b) { return accurate::lessOrEqualsOp(a, b); } }; template struct GreaterOrEqualsOp { using SymmetricOp = LessOrEqualsOp; static UInt8 apply(A a, B b) { return accurate::greaterOrEqualsOp(a, b); } }; template struct ReversedCmpOp; template struct CmpOp { using SymmetricOp = ReversedCmpOp; static Int8 apply(A a, B b) { return accurate::equalsOp(a, b) ? 0 : (accurate::lessOp(a, b) ? -1 : 1); } }; template struct ReversedCmpOp { using SymmetricOp = CmpOp; static Int8 apply(A a, B b) { return SymmetricOp::apply(b, a); } }; } // namespace DB