557 changes: 557 additions & 0 deletions libc/docs/math/log.rst

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"ldexpf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntType>]>,
FunctionSpec<"ldexpl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>]>,

FunctionSpec<"log10", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
FunctionSpec<"log10f", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

FunctionSpec<"log1pf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
Expand Down
10 changes: 10 additions & 0 deletions libc/src/__support/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ add_header_library(
ROUND_OPT
)

add_header_library(
double_double
HDRS
double_double.h
DEPENDS
libc.src.__support.common
libc.src.__support.number_pair
.multiply_add
)

add_header_library(
dyadic_float
HDRS
Expand Down
52 changes: 52 additions & 0 deletions libc/src/__support/FPUtil/double_double.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===-- Utilities for double-double data type. ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_DOUBLEDOUBLE_H
#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_DOUBLEDOUBLE_H

#include "multiply_add.h"
#include "src/__support/number_pair.h"

namespace __llvm_libc::fputil {

using DoubleDouble = __llvm_libc::NumberPair<double>;

// Assumption: |a| >= |b|
constexpr inline DoubleDouble exact_add(double a, double b) {
DoubleDouble r{0.0, 0.0};
r.hi = a + b;
double t = r.hi - a;
r.lo = b - t;
return r;
}

// Assumption: |a.hi| >= |b.hi|
constexpr inline DoubleDouble add(DoubleDouble a, DoubleDouble b) {
DoubleDouble r = exact_add(a.hi, b.hi);
double lo = a.lo + b.lo;
return exact_add(r.hi, r.lo + lo);
}

// Assumption: |a.hi| >= |b|
constexpr inline DoubleDouble add(DoubleDouble a, double b) {
DoubleDouble r = exact_add(a.hi, b);
return exact_add(r.hi, r.lo + a.lo);
}

// TODO(lntue): add a correct multiplication when FMA instructions are not
// available.
constexpr inline DoubleDouble exact_mult(double a, double b) {
DoubleDouble r{0.0, 0.0};
r.hi = a * b;
r.lo = fputil::multiply_add(a, b, -r.hi);
return r;
}

} // namespace __llvm_libc::fputil

#endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_DOUBLEDOUBLE_H
2 changes: 1 addition & 1 deletion libc/src/__support/FPUtil/dyadic_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ template <size_t Bits> struct DyadicFloat {
DyadicFloat(bool s, int e, MantissaType m)
: sign(s), exponent(e), mantissa(m) {
normalize();
};
}

// Normalizing the mantissa, bringing the leading 1 bit to the most
// significant bit.
Expand Down
2 changes: 0 additions & 2 deletions libc/src/__support/number_pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ namespace __llvm_libc {

DEFINE_NAMED_PAIR_TEMPLATE(NumberPair, lo, hi);

using DoubleDouble = NumberPair<double>;

template <typename T>
cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, NumberPair<T>>
split(T a) {
Expand Down
1 change: 1 addition & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ add_math_entrypoint_object(ldexp)
add_math_entrypoint_object(ldexpf)
add_math_entrypoint_object(ldexpl)

add_math_entrypoint_object(log10)
add_math_entrypoint_object(log10f)

add_math_entrypoint_object(log1pf)
Expand Down
16 changes: 16 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,22 @@ add_object_library(
common_constants.cpp
)

# TODO(lntue): Make log10 correctly rounded for non-FMA targets.
add_entrypoint_object(
log10
SRCS
log10.cpp
HDRS
../log10.h
DEPENDS
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.double_double
libc.src.__support.FPUtil.dyadic_float
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
log10f
SRCS
Expand Down
1,084 changes: 1,084 additions & 0 deletions libc/src/math/generic/log10.cpp

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions libc/src/math/log10.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for log10 -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_LOG10_H
#define LLVM_LIBC_SRC_MATH_LOG10_H

namespace __llvm_libc {

double log10(double x);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_MATH_LOG10_H
17 changes: 17 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,23 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
log10_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
log10_test.cpp
DEPENDS
libc.include.errno
libc.src.errno.errno
libc.include.math
libc.src.math.log10
libc.src.__support.FPUtil.fp_bits
FLAGS
FMA_OPT__ONLY
)

add_fp_unittest(
log10f_test
NEED_MPFR
Expand Down
125 changes: 125 additions & 0 deletions libc/test/src/math/log10_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//===-- Unittests for log10 -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/FPUtil/FPBits.h"
#include "src/math/log10.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h"
#include "utils/UnitTest/Test.h"
#include "utils/testutils/StreamWrapper.h"
#include <math.h>

#include <errno.h>
#include <stdint.h>

namespace mpfr = __llvm_libc::testing::mpfr;
auto outs = __llvm_libc::testutils::outs();

DECLARE_SPECIAL_CONSTANTS(double)

TEST(LlvmLibcLog10Test, SpecialNumbers) {
EXPECT_FP_EQ(aNaN, __llvm_libc::log10(aNaN));
EXPECT_FP_EQ(inf, __llvm_libc::log10(inf));
EXPECT_TRUE(FPBits(__llvm_libc::log10(neg_inf)).is_nan());
EXPECT_FP_EQ(neg_inf, __llvm_libc::log10(0.0));
EXPECT_FP_EQ(neg_inf, __llvm_libc::log10(-0.0));
EXPECT_TRUE(FPBits(__llvm_libc::log10(-1.0)).is_nan());
EXPECT_FP_EQ(zero, __llvm_libc::log10(1.0));
}

TEST(LlvmLibcLog10Test, TrickyInputs) {
constexpr int N = 27;
constexpr uint64_t INPUTS[N] = {
0x3ff0000000000000, // x = 1.0
0x4024000000000000, // x = 10.0
0x4059000000000000, // x = 10^2
0x408f400000000000, // x = 10^3
0x40c3880000000000, // x = 10^4
0x40f86a0000000000, // x = 10^5
0x412e848000000000, // x = 10^6
0x416312d000000000, // x = 10^7
0x4197d78400000000, // x = 10^8
0x41cdcd6500000000, // x = 10^9
0x4202a05f20000000, // x = 10^10
0x42374876e8000000, // x = 10^11
0x426d1a94a2000000, // x = 10^12
0x42a2309ce5400000, // x = 10^13
0x42d6bcc41e900000, // x = 10^14
0x430c6bf526340000, // x = 10^15
0x4341c37937e08000, // x = 10^16
0x4376345785d8a000, // x = 10^17
0x43abc16d674ec800, // x = 10^18
0x43e158e460913d00, // x = 10^19
0x4415af1d78b58c40, // x = 10^20
0x444b1ae4d6e2ef50, // x = 10^21
0x4480f0cf064dd592, // x = 10^22
0x3fefffffffef06ad, 0x3fefde0f22c7d0eb,
0x225e7812faadb32f, 0x3fee1076964c2903,
};
for (int i = 0; i < N; ++i) {
double x = double(FPBits(INPUTS[i]));
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log10, x,
__llvm_libc::log10(x), 0.5);
}
}

TEST(LlvmLibcLog10Test, InDoubleRange) {
constexpr uint64_t COUNT = 1234561;
constexpr uint64_t STEP = 0x3FF0'0000'0000'0000ULL / COUNT;

auto test = [&](mpfr::RoundingMode rounding_mode) {
mpfr::ForceRoundingMode __r(rounding_mode);
uint64_t fails = 0;
uint64_t count = 0;
uint64_t cc = 0;
double mx, mr = 0.0;
double tol = 0.5;

for (uint64_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
double x = FPBits(v).get_val();
if (isnan(x) || isinf(x) || x < 0.0)
continue;
errno = 0;
double result = __llvm_libc::log10(x);
++cc;
if (isnan(result))
continue;

++count;
// ASSERT_MPFR_MATCH(mpfr::Operation::Log10, x, result, 0.5);
if (!EXPECT_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Log10, x, result,
0.5, rounding_mode)) {
++fails;
while (!EXPECT_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Log10, x,
result, tol, rounding_mode)) {
mx = x;
mr = result;
tol *= 2.0;
}
}
}
outs << " Log10 failed: " << fails << "/" << count << "/" << cc
<< " tests.\n";
outs << " Max ULPs is at most: " << tol << ".\n";
if (fails) {
EXPECT_MPFR_MATCH(mpfr::Operation::Log10, mx, mr, 0.5, rounding_mode);
}
};

outs << " Test Rounding To Nearest...\n";
test(mpfr::RoundingMode::Nearest);

outs << " Test Rounding Downward...\n";
test(mpfr::RoundingMode::Downward);

outs << " Test Rounding Upward...\n";
test(mpfr::RoundingMode::Upward);

outs << " Test Rounding Toward Zero...\n";
test(mpfr::RoundingMode::TowardZero);
}