sql_utils/base/mathutil.cc (54 lines of code) (raw):

/* * Copyright 2023 Google LLC * * 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. */ #include "sql_utils/base/mathutil.h" #include <stdlib.h> #include <limits> #include <vector> #include "absl/base/casts.h" #include "sql_utils/base/logging.h" namespace bigquery_ml_utils_base { namespace { template <typename R, typename F, typename Rep> R DecomposeImpl(const F value) { using Limits = std::numeric_limits<F>; using Mantissa = decltype(R{}.mantissa); using Exponent = decltype(R{}.exponent); // The leading one-bit in 1.xxxxxx is not stored. constexpr int kMantissaBits = Limits::digits - 1; constexpr Rep kSignMask = ~(std::numeric_limits<Rep>::max() >> 1); constexpr Rep kMantissaMask = (Rep{1} << kMantissaBits) - 1; static_assert(Limits::is_iec559, "Floating point must follow IEC 559 (IEEE 754) standard."); static_assert(Limits::has_denorm == std::denorm_present, "Floating point must support denormal values."); static_assert(sizeof(F) * 8 - kMantissaBits <= sizeof(Exponent) * 8, "Exponent bits do not fit in Exponent's type"); if (!std::isfinite(value)) { const int exponent = std::numeric_limits<Exponent>::max(); if (value == Limits::infinity()) { return {std::numeric_limits<Mantissa>::max(), exponent}; } if (value == -Limits::infinity()) { return {-std::numeric_limits<Mantissa>::max(), exponent}; } // value is NaN. return {0, exponent}; } const Rep bits = absl::bit_cast<Mantissa>(value); R parts; parts.mantissa = static_cast<Mantissa>(bits & kMantissaMask); parts.exponent = static_cast<Exponent>((bits & ~kSignMask) >> kMantissaBits); if (parts.exponent > 0) { // The value is normal, and represented as // 1.(mantissa bits) * pow(2, exponent - exponent_bias) // We need to add the implicit leading one-bit to mantissa. // The exponent offset is different from the denormal case by one, // so we account for it here as well. --parts.exponent; parts.mantissa |= (kMantissaMask + 1); } // Else, value is 0 or denormal. // The value is 0.(mantissa bits) * pow(2, 0 - (exponent_bias - 1)). if ((bits & kSignMask) != 0) { parts.mantissa = -parts.mantissa; } parts.exponent -= Limits::digits - Limits::min_exponent; return parts; } } // namespace MathUtil::FloatParts MathUtil::Decompose(float value) { return DecomposeImpl<FloatParts, float, uint32_t>(value); } MathUtil::DoubleParts MathUtil::Decompose(double value) { return DecomposeImpl<DoubleParts, double, uint64_t>(value); } } // namespace bigquery_ml_utils_base