You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PP directive: -DBOOST_INT128_NO_BUILTIN_INT128 (see below)
Builder: CMake 3.28.3
Summary
Important
Verdict: accepted, conditionally
Design: Beside caveats described hereafter, the API feels intuitive and simple for common use-cases. constexpr (especially for C++14) and NTTP support are expected yet great additions too.
Documentation: https://develop.int128.cpp.al/ looks way better than what I'm used to with other Boost libraries (before its modernisation). The search bar and preview are convenient/efficient.
Testing: Looks very decent.
Accessibility: The standalone header working on godbolt (compilation time) is great to experiment fast.
Disclaimers
I did not audit the numerical algorithms behind the operators (Knuth's Algorithm D and friends).
This review is written from a user's point of view: the public API, and the behaviors most users will actually expect/hit.
I tested the library for my personnal use-cases, using C++23 not C++14.
This review is partial, as a best-effort on my spare time.
constexpr boost::int128::uint128_t value{1, 0}; // same with int128_tconstexprbool a = value; // copy-initializationstatic_assert(false == a); // unexpected !constexprboolb(value); // direct-initializationstatic_assert(b); // okconstexprbool c = static_cast<bool>(value);
static_assert(c);
ifconstexpr (value){
static_assert(true);
}
f(value);
std::vector<bool> vec;
vec.push_back(value); // vec[0] == false// as a comparison, with compilers intrinsinc __int128:constexprunsigned __int128 u = (unsigned __int128)1 << 64;
constexprbool d = u;
static_assert(d);
Details:
bool satisfies is_integral && is_unsigned, so the templated constrained operator UnsignedInteger() is a viable conversion to bool and returns static_cast<bool>(low).
In copy-initialization the explicit operator bool() is not a candidate, so the template is the only one, and it drops high, resulting in a behavior that feels very unexpected/inconsistent.
Headers disable -Wsign-conversion for the rest of the TU, which can have impacts on user-side by silencing relevant/important warnings.
Files:
detail/uint128_imp.hpp:403-408 pushes and never pops.
detail/int128_imp.hpp (with Clang) also nets one extra push: pushes at the line 445 (-Wsign-conversion, -Wsign-compare) and 2151 (-Wassume), single pop at line 2371.
// g++ : warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]// clang++ : warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]unsignedf(int x){ return x; }
#include<boost/int128.hpp>unsignedg(int x){ return x; } // no warnings even with `-Wsign-conversion`automain() -> int {
[[maybe_unused]]auto f_result = f(42);
[[maybe_unused]]auto g_result = g(42);
}
3. saturating_mul wrong branch
Disclaimer: Not a math guy, but from my understanding:
saturating_mul(a, b); // == a * b, whenever a * b fitssaturating_mul(a, b); // == BOOST_INT128_UINT128_MAX, otherwise
Reviews: Boost.Int128
develop, on5aa7c7f-DBOOST_INT128_NO_BUILTIN_INT128(see below)Summary
Important
Verdict: accepted, conditionally
constexpr(especially for C++14) and NTTP support are expected yet great additions too.Content
Bugs & suprising behaviors
1. Inconsistent conversion to
boolFiles:
detail/uint128_imp.hpp:115-121detail/int128_imp.hpp:109-115Demo: https://godbolt.org/z/1P9szYT4z
Details:
boolsatisfiesis_integral && is_unsigned, so the templated constrainedoperator UnsignedInteger()is a viable conversion tobooland returnsstatic_cast<bool>(low).explicit operator bool()is not a candidate, so the template is the only one, and it dropshigh, resulting in a behavior that feels very unexpected/inconsistent.See
2. Preprocessor-directive side-effects
Headers disable
-Wsign-conversionfor the rest of the TU, which can have impacts on user-side by silencing relevant/important warnings.Files:
detail/uint128_imp.hpp:403-408pushes and never pops.detail/int128_imp.hpp(with Clang) also nets one extra push: pushes at the line 445 (-Wsign-conversion,-Wsign-compare) and 2151 (-Wassume), single pop at line 2371.Demo: https://godbolt.org/z/vren91jKb
3.
saturating_mulwrong branchDisclaimer: Not a math guy, but from my understanding:
Demo: https://godbolt.org/z/Y6TW9458e
4.
BOOST_INT128_NO_BUILTIN_INT128gets ignored if/whenBOOST_HAS_INT128is definedWhat:
File:
detail/config.hpp:28doc/modules/ROOT/pages/config.adoc:28Demo: https://godbolt.org/z/8faorTs8c
# commands g++ -std=c++23 -O2 test.cpp g++ -std=c++23 -O2 -DBOOST_INT128_NO_BUILTIN_INT128 test.cpp g++ -std=c++23 -O2 -DBOOST_HAS_INT128 -DBOOST_INT128_NO_BUILTIN_INT128 test.cppSuggested fix:
#if (defined(BOOST_HAS_INT128) || (defined(__SIZEOF_INT128__) && !defined(_MSC_VER)) && !defined(BOOST_INT128_NO_BUILTIN_INT128)) && !defined(__SYCL_DEVICE_ONLY__)should probably add parenthesis around
||, see demo https://godbolt.org/z/bWKMbK1EE:5.
boost::random::traits::make_unsigned<int128_t>::typeresult inint128_tFile:
include/boost/int128/random.hpp:39.What:
This feels unintuitive/misleading
Suggested fix:
API
Ambiguous overload resolutions:
__int128vs.uint128_tDemo: https://godbolt.org/z/fxbThdfeT
Ambiguous overload resolutions feel like inconsistency when comparing
uint128_tto__int128usage:uint128_tunsigned __int128v + 1.0ambiguous overload for 'operator+' (operand types are 'uint128_t' and 'double')v < 1.0ambiguous overload for 'operator<'double d; d += v;ambiguous overload for 'operator+='int i; i |= v;ambiguous overload for 'operator|='switch (v)ambiguous default type conversion from 'uint128_t'u128 <=> i128ambiguous overload for 'operator<=>'Division by zero: binary operations are inconsistent with operator overloads
File:
include/boost/int128/cstdlib.hpp:25include/boost/int128/detail/int128_imp.hpp:2206Demo: https://godbolt.org/z/3YdMv3h7x
Suggested fix:
Formatting support: questionable dependency to
Boost.DecimalFile:
include/boost/int128/format.hpp:8Considering the guard
#if __has_include(<format>) && defined(__cpp_lib_format) && __cpp_lib_format >= 201907L && !defined(BOOST_DECIMAL_DISABLE_CLIB)then
BOOST_INT128_HAS_FORMATwon't be defined ifBOOST_DECIMAL_DISABLE_CLIBis defined, which feels misleading/questionable.Neat
boost::int128::uint128_tfeels odd to write ->uint128type nested in aint128namespace.