Fix "unsafe narrowing" warnings in absl, 5/n.

Addresses failures with the following, in some files:
-Wshorten-64-to-32
-Wimplicit-int-conversion
-Wsign-compare
-Wsign-conversion
-Wtautological-unsigned-zero-compare

(This specific CL focuses on .cc files in strings/internal/.)

Bug: chromium:1292951
PiperOrigin-RevId: 468215101
Change-Id: I07fa487bcf2cf62d403489c3be7a5997cdef8987
pull/1252/head
Abseil Team 3 years ago committed by Copybara-Service
parent 934f613818
commit fcfc7a6d15
  1. 8
      absl/strings/internal/charconv_bigint.cc
  2. 4
      absl/strings/internal/charconv_parse.cc
  3. 5
      absl/strings/internal/cord_internal.h
  4. 7
      absl/strings/internal/cord_rep_btree.cc
  5. 5
      absl/strings/internal/cord_rep_btree.h
  6. 10
      absl/strings/internal/cord_rep_btree_navigator.cc
  7. 13
      absl/strings/internal/cordz_info.cc
  8. 8
      absl/strings/internal/cordz_info.h
  9. 8
      absl/strings/internal/cordz_statistics.h
  10. 15
      absl/strings/internal/memutil.cc
  11. 35
      absl/strings/internal/str_format/arg.cc
  12. 5
      absl/strings/internal/str_format/bind.cc
  13. 3
      absl/strings/internal/str_format/extension.cc
  14. 390
      absl/strings/internal/str_format/float_conversion.cc

@ -242,7 +242,7 @@ int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end,
// decimal exponent to compensate. // decimal exponent to compensate.
--exponent_adjust; --exponent_adjust;
} }
int digit = (*begin - '0'); char digit = (*begin - '0');
--significant_digits; --significant_digits;
if (significant_digits == 0 && std::next(begin) != end && if (significant_digits == 0 && std::next(begin) != end &&
(digit == 0 || digit == 5)) { (digit == 0 || digit == 5)) {
@ -255,7 +255,7 @@ int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end,
// 500000...000000000001 to correctly round up, rather than to nearest. // 500000...000000000001 to correctly round up, rather than to nearest.
++digit; ++digit;
} }
queued = 10 * queued + digit; queued = 10 * queued + static_cast<uint32_t>(digit);
++digits_queued; ++digits_queued;
if (digits_queued == kMaxSmallPowerOfTen) { if (digits_queued == kMaxSmallPowerOfTen) {
MultiplyBy(kTenToNth[kMaxSmallPowerOfTen]); MultiplyBy(kTenToNth[kMaxSmallPowerOfTen]);
@ -341,8 +341,8 @@ std::string BigUnsigned<max_words>::ToString() const {
std::string result; std::string result;
// Build result in reverse order // Build result in reverse order
while (copy.size() > 0) { while (copy.size() > 0) {
int next_digit = copy.DivMod<10>(); uint32_t next_digit = copy.DivMod<10>();
result.push_back('0' + next_digit); result.push_back('0' + static_cast<char>(next_digit));
} }
if (result.empty()) { if (result.empty()) {
result.push_back('0'); result.push_back('0');

@ -190,11 +190,11 @@ bool IsDigit<16>(char ch) {
template <> template <>
unsigned ToDigit<10>(char ch) { unsigned ToDigit<10>(char ch) {
return ch - '0'; return static_cast<unsigned>(ch - '0');
} }
template <> template <>
unsigned ToDigit<16>(char ch) { unsigned ToDigit<16>(char ch) {
return kAsciiToInt[static_cast<unsigned char>(ch)]; return static_cast<unsigned>(kAsciiToInt[static_cast<unsigned char>(ch)]);
} }
template <> template <>

@ -129,8 +129,9 @@ class RefcountAndFlags {
} }
// Returns the current reference count using acquire semantics. // Returns the current reference count using acquire semantics.
inline int32_t Get() const { inline size_t Get() const {
return count_.load(std::memory_order_acquire) >> kNumFlags; return static_cast<size_t>(count_.load(std::memory_order_acquire) >>
kNumFlags);
} }
// Returns whether the atomic integer is 1. // Returns whether the atomic integer is 1.

@ -17,6 +17,7 @@
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include <ostream>
#include <string> #include <string>
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
@ -55,8 +56,10 @@ inline bool exhaustive_validation() {
// Prints the entire tree structure or 'rep'. External callers should // Prints the entire tree structure or 'rep'. External callers should
// not specify 'depth' and leave it to its default (0) value. // not specify 'depth' and leave it to its default (0) value.
// Rep may be a CordRepBtree tree, or a SUBSTRING / EXTERNAL / FLAT node. // Rep may be a CordRepBtree tree, or a SUBSTRING / EXTERNAL / FLAT node.
void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, void DumpAll(const CordRep* rep,
int depth = 0) { bool include_contents,
std::ostream& stream,
size_t depth = 0) {
// Allow for full height trees + substring -> flat / external nodes. // Allow for full height trees + substring -> flat / external nodes.
assert(depth <= CordRepBtree::kMaxDepth + 2); assert(depth <= CordRepBtree::kMaxDepth + 2);
std::string sharing = const_cast<CordRep*>(rep)->refcount.IsOne() std::string sharing = const_cast<CordRep*>(rep)->refcount.IsOne()

@ -95,8 +95,9 @@ class CordRepBtree : public CordRep {
// local stack variable compared to Cord's current near 400 bytes stack use. // local stack variable compared to Cord's current near 400 bytes stack use.
// The maximum `height` value of a node is then `kMaxDepth - 1` as node height // The maximum `height` value of a node is then `kMaxDepth - 1` as node height
// values start with a value of 0 for leaf nodes. // values start with a value of 0 for leaf nodes.
static constexpr int kMaxDepth = 12; static constexpr size_t kMaxDepth = 12;
static constexpr int kMaxHeight = kMaxDepth - 1; // See comments on height() for why this is an int and not a size_t.
static constexpr int kMaxHeight = static_cast<int>(kMaxDepth - 1);
// `Action` defines the action for unwinding changes done at the btree's leaf // `Action` defines the action for unwinding changes done at the btree's leaf
// level that need to be propagated up to the parent node(s). Each operation // level that need to be propagated up to the parent node(s). Each operation

@ -90,7 +90,7 @@ CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) {
// edges that must be skipped. // edges that must be skipped.
while (height > 0) { while (height > 0) {
node = edge->btree(); node = edge->btree();
index_[height] = index; index_[height] = static_cast<uint8_t>(index);
node_[--height] = node; node_[--height] = node;
index = node->begin(); index = node->begin();
edge = node->Edge(index); edge = node->Edge(index);
@ -101,7 +101,7 @@ CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) {
edge = node->Edge(index); edge = node->Edge(index);
} }
} }
index_[0] = index; index_[0] = static_cast<uint8_t>(index);
return {edge, n}; return {edge, n};
} }
@ -126,7 +126,7 @@ ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) {
do { do {
length -= edge->length; length -= edge->length;
while (++index == node->end()) { while (++index == node->end()) {
index_[height] = index; index_[height] = static_cast<uint8_t>(index);
if (++height > height_) { if (++height > height_) {
subtree->set_end(subtree_end); subtree->set_end(subtree_end);
if (length == 0) return {subtree, 0}; if (length == 0) return {subtree, 0};
@ -154,7 +154,7 @@ ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) {
// edges that must be read, adding 'down' nodes to `subtree`. // edges that must be read, adding 'down' nodes to `subtree`.
while (height > 0) { while (height > 0) {
node = edge->btree(); node = edge->btree();
index_[height] = index; index_[height] = static_cast<uint8_t>(index);
node_[--height] = node; node_[--height] = node;
index = node->begin(); index = node->begin();
edge = node->Edge(index); edge = node->Edge(index);
@ -178,7 +178,7 @@ ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) {
subtree->edges_[subtree_end++] = Substring(edge, 0, length); subtree->edges_[subtree_end++] = Substring(edge, 0, length);
} }
subtree->set_end(subtree_end); subtree->set_end(subtree_end);
index_[0] = index; index_[0] = static_cast<uint8_t>(index);
return {tree, length}; return {tree, length};
} }

@ -35,7 +35,7 @@ namespace cord_internal {
using ::absl::base_internal::SpinLockHolder; using ::absl::base_internal::SpinLockHolder;
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr int CordzInfo::kMaxStackDepth; constexpr size_t CordzInfo::kMaxStackDepth;
#endif #endif
ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit}; ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit};
@ -291,7 +291,7 @@ CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) {
: src->method_; : src->method_;
} }
int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { size_t CordzInfo::FillParentStack(const CordzInfo* src, void** stack) {
assert(stack); assert(stack);
if (src == nullptr) return 0; if (src == nullptr) return 0;
if (src->parent_stack_depth_) { if (src->parent_stack_depth_) {
@ -302,11 +302,14 @@ int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) {
return src->stack_depth_; return src->stack_depth_;
} }
CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, CordzInfo::CordzInfo(CordRep* rep,
const CordzInfo* src,
MethodIdentifier method) MethodIdentifier method)
: rep_(rep), : rep_(rep),
stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, stack_depth_(
/*skip_count=*/1)), static_cast<size_t>(absl::GetStackTrace(stack_,
/*max_depth=*/kMaxStackDepth,
/*skip_count=*/1))),
parent_stack_depth_(FillParentStack(src, parent_stack_)), parent_stack_depth_(FillParentStack(src, parent_stack_)),
method_(method), method_(method),
parent_method_(GetParentMethod(src)), parent_method_(GetParentMethod(src)),

@ -196,7 +196,7 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
std::atomic<CordzInfo*> head ABSL_GUARDED_BY(mutex){nullptr}; std::atomic<CordzInfo*> head ABSL_GUARDED_BY(mutex){nullptr};
}; };
static constexpr int kMaxStackDepth = 64; static constexpr size_t kMaxStackDepth = 64;
explicit CordzInfo(CordRep* rep, const CordzInfo* src, explicit CordzInfo(CordRep* rep, const CordzInfo* src,
MethodIdentifier method); MethodIdentifier method);
@ -216,7 +216,7 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
// `stack_` depending on `parent_stack_` being empty, returning the size of // `stack_` depending on `parent_stack_` being empty, returning the size of
// the parent stack. // the parent stack.
// Returns 0 if `src` is null. // Returns 0 if `src` is null.
static int FillParentStack(const CordzInfo* src, void** stack); static size_t FillParentStack(const CordzInfo* src, void** stack);
void ODRCheck() const { void ODRCheck() const {
#ifndef NDEBUG #ifndef NDEBUG
@ -244,8 +244,8 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
void* stack_[kMaxStackDepth]; void* stack_[kMaxStackDepth];
void* parent_stack_[kMaxStackDepth]; void* parent_stack_[kMaxStackDepth];
const int stack_depth_; const size_t stack_depth_;
const int parent_stack_depth_; const size_t parent_stack_depth_;
const MethodIdentifier method_; const MethodIdentifier method_;
const MethodIdentifier parent_method_; const MethodIdentifier parent_method_;
CordzUpdateTracker update_tracker_; CordzUpdateTracker update_tracker_;

@ -45,12 +45,12 @@ struct CordzStatistics {
}; };
// The size of the cord in bytes. This matches the result of Cord::size(). // The size of the cord in bytes. This matches the result of Cord::size().
int64_t size = 0; size_t size = 0;
// The estimated memory used by the sampled cord. This value matches the // The estimated memory used by the sampled cord. This value matches the
// value as reported by Cord::EstimatedMemoryUsage(). // value as reported by Cord::EstimatedMemoryUsage().
// A value of 0 implies the property has not been recorded. // A value of 0 implies the property has not been recorded.
int64_t estimated_memory_usage = 0; size_t estimated_memory_usage = 0;
// The effective memory used by the sampled cord, inversely weighted by the // The effective memory used by the sampled cord, inversely weighted by the
// effective indegree of each allocated node. This is a representation of the // effective indegree of each allocated node. This is a representation of the
@ -59,14 +59,14 @@ struct CordzStatistics {
// by multiple Cord instances, and for cases where a Cord includes the same // by multiple Cord instances, and for cases where a Cord includes the same
// node multiple times (either directly or indirectly). // node multiple times (either directly or indirectly).
// A value of 0 implies the property has not been recorded. // A value of 0 implies the property has not been recorded.
int64_t estimated_fair_share_memory_usage = 0; size_t estimated_fair_share_memory_usage = 0;
// The total number of nodes referenced by this cord. // The total number of nodes referenced by this cord.
// For ring buffer Cords, this includes the 'ring buffer' node. // For ring buffer Cords, this includes the 'ring buffer' node.
// For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all
// the substring, flat and external nodes referenced by the tree. // the substring, flat and external nodes referenced by the tree.
// A value of 0 implies the property has not been recorded. // A value of 0 implies the property has not been recorded.
int64_t node_count = 0; size_t node_count = 0;
// Detailed node counts per type // Detailed node counts per type
NodeCounts node_counts; NodeCounts node_counts;

@ -54,10 +54,11 @@ size_t memspn(const char* s, size_t slen, const char* accept) {
cont: cont:
c = *p++; c = *p++;
if (slen-- == 0) return p - 1 - s; if (slen-- == 0)
return static_cast<size_t>(p - 1 - s);
for (spanp = accept; (sc = *spanp++) != '\0';) for (spanp = accept; (sc = *spanp++) != '\0';)
if (sc == c) goto cont; if (sc == c) goto cont;
return p - 1 - s; return static_cast<size_t>(p - 1 - s);
} }
size_t memcspn(const char* s, size_t slen, const char* reject) { size_t memcspn(const char* s, size_t slen, const char* reject) {
@ -68,9 +69,10 @@ size_t memcspn(const char* s, size_t slen, const char* reject) {
while (slen-- != 0) { while (slen-- != 0) {
c = *p++; c = *p++;
for (spanp = reject; (sc = *spanp++) != '\0';) for (spanp = reject; (sc = *spanp++) != '\0';)
if (sc == c) return p - 1 - s; if (sc == c)
return static_cast<size_t>(p - 1 - s);
} }
return p - s; return static_cast<size_t>(p - s);
} }
char* mempbrk(const char* s, size_t slen, const char* accept) { char* mempbrk(const char* s, size_t slen, const char* accept) {
@ -97,8 +99,9 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
const char* hayend = phaystack + haylen - neelen + 1; const char* hayend = phaystack + haylen - neelen + 1;
// A static cast is used here to work around the fact that memchr returns // A static cast is used here to work around the fact that memchr returns
// a void* on Posix-compliant systems and const void* on Windows. // a void* on Posix-compliant systems and const void* on Windows.
while ((match = static_cast<const char*>( while (
memchr(phaystack, pneedle[0], hayend - phaystack)))) { (match = static_cast<const char*>(memchr(
phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) {
if (memcmp(match, pneedle, neelen) == 0) if (memcmp(match, pneedle, neelen) == 0)
return match; return match;
else else

@ -77,7 +77,7 @@ class IntDigits {
v >>= 3; v >>= 3;
} while (v); } while (v);
start_ = p; start_ = p;
size_ = storage_ + sizeof(storage_) - p; size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
} }
// Print the signed or unsigned integer as decimal. // Print the signed or unsigned integer as decimal.
@ -86,7 +86,8 @@ class IntDigits {
void PrintAsDec(T v) { void PrintAsDec(T v) {
static_assert(std::is_integral<T>::value, ""); static_assert(std::is_integral<T>::value, "");
start_ = storage_; start_ = storage_;
size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; size_ = static_cast<size_t>(numbers_internal::FastIntToBuffer(v, storage_) -
storage_);
} }
void PrintAsDec(int128 v) { void PrintAsDec(int128 v) {
@ -115,7 +116,7 @@ class IntDigits {
if (add_neg) { if (add_neg) {
*--p = '-'; *--p = '-';
} }
size_ = storage_ + sizeof(storage_) - p; size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
start_ = p; start_ = p;
} }
@ -138,7 +139,7 @@ class IntDigits {
++p; ++p;
} }
start_ = p; start_ = p;
size_ = storage_ + sizeof(storage_) - p; size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
} }
// Print the unsigned integer as hex using uppercase. // Print the unsigned integer as hex using uppercase.
@ -154,7 +155,7 @@ class IntDigits {
v >>= 4; v >>= 4;
} while (v); } while (v);
start_ = p; start_ = p;
size_ = storage_ + sizeof(storage_) - p; size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
} }
// The printed value including the '-' sign if available. // The printed value including the '-' sign if available.
@ -208,10 +209,12 @@ string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) {
return {}; return {};
} }
bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, bool ConvertCharImpl(char v,
FormatSinkImpl *sink) { const FormatConversionSpecImpl conv,
FormatSinkImpl* sink) {
size_t fill = 0; size_t fill = 0;
if (conv.width() >= 0) fill = conv.width(); if (conv.width() >= 0)
fill = static_cast<size_t>(conv.width());
ReducePadding(1, &fill); ReducePadding(1, &fill);
if (!conv.has_left_flag()) sink->Append(fill, ' '); if (!conv.has_left_flag()) sink->Append(fill, ' ');
sink->Append(1, v); sink->Append(1, v);
@ -225,7 +228,8 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
// Print as a sequence of Substrings: // Print as a sequence of Substrings:
// [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
size_t fill = 0; size_t fill = 0;
if (conv.width() >= 0) fill = conv.width(); if (conv.width() >= 0)
fill = static_cast<size_t>(conv.width());
string_view formatted = as_digits.without_neg_or_zero(); string_view formatted = as_digits.without_neg_or_zero();
ReducePadding(formatted, &fill); ReducePadding(formatted, &fill);
@ -236,10 +240,9 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
string_view base_indicator = BaseIndicator(as_digits, conv); string_view base_indicator = BaseIndicator(as_digits, conv);
ReducePadding(base_indicator, &fill); ReducePadding(base_indicator, &fill);
int precision = conv.precision(); bool precision_specified = conv.precision() >= 0;
bool precision_specified = precision >= 0; size_t precision =
if (!precision_specified) precision_specified ? static_cast<size_t>(conv.precision()) : size_t{1};
precision = 1;
if (conv.has_alt_flag() && if (conv.has_alt_flag() &&
conv.conversion_char() == FormatConversionCharInternal::o) { conv.conversion_char() == FormatConversionCharInternal::o) {
@ -247,7 +250,7 @@ bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
// "For o conversion, it increases the precision (if necessary) to // "For o conversion, it increases the precision (if necessary) to
// force the first digit of the result to be zero." // force the first digit of the result to be zero."
if (formatted.empty() || *formatted.begin() != '0') { if (formatted.empty() || *formatted.begin() != '0') {
int needed = static_cast<int>(formatted.size()) + 1; size_t needed = formatted.size() + 1;
precision = std::max(precision, needed); precision = std::max(precision, needed);
} }
} }
@ -287,7 +290,7 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv,
// FormatConversionChar is declared, but not defined. // FormatConversionChar is declared, but not defined.
switch (static_cast<uint8_t>(conv.conversion_char())) { switch (static_cast<uint8_t>(conv.conversion_char())) {
case static_cast<uint8_t>(FormatConversionCharInternal::c): case static_cast<uint8_t>(FormatConversionCharInternal::c):
return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); return ConvertCharImpl(static_cast<char>(v), conv, sink);
case static_cast<uint8_t>(FormatConversionCharInternal::o): case static_cast<uint8_t>(FormatConversionCharInternal::o):
as_digits.PrintAsOct(static_cast<U>(v)); as_digits.PrintAsOct(static_cast<U>(v));
@ -375,7 +378,7 @@ FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
len = std::strlen(v); len = std::strlen(v);
} else { } else {
// If precision is set, we look for the NUL-terminator on the valid range. // If precision is set, we look for the NUL-terminator on the valid range.
len = std::find(v, v + conv.precision(), '\0') - v; len = static_cast<size_t>(std::find(v, v + conv.precision(), '\0') - v);
} }
return {ConvertStringArg(string_view(v, len), conv, sink)}; return {ConvertStringArg(string_view(v, len), conv, sink)};
} }

@ -32,7 +32,8 @@ inline bool BindFromPosition(int position, int* value,
return false; return false;
} }
// -1 because positions are 1-based // -1 because positions are 1-based
return FormatArgImplFriend::ToInt(pack[position - 1], value); return FormatArgImplFriend::ToInt(pack[static_cast<size_t>(position) - 1],
value);
} }
class ArgContext { class ArgContext {
@ -56,7 +57,7 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound,
const FormatArgImpl* arg = nullptr; const FormatArgImpl* arg = nullptr;
int arg_position = unbound->arg_position; int arg_position = unbound->arg_position;
if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false; if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
arg = &pack_[arg_position - 1]; // 1-based arg = &pack_[static_cast<size_t>(arg_position - 1)]; // 1-based
if (unbound->flags != Flags::kBasic) { if (unbound->flags != Flags::kBasic) {
int width = unbound->width.value(); int width = unbound->width.value();

@ -58,7 +58,8 @@ constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer;
bool FormatSinkImpl::PutPaddedString(string_view value, int width, bool FormatSinkImpl::PutPaddedString(string_view value, int width,
int precision, bool left) { int precision, bool left) {
size_t space_remaining = 0; size_t space_remaining = 0;
if (width >= 0) space_remaining = width; if (width >= 0)
space_remaining = static_cast<size_t>(width);
size_t n = value.size(); size_t n = value.size();
if (precision >= 0) n = std::min(n, static_cast<size_t>(precision)); if (precision >= 0) n = std::min(n, static_cast<size_t>(precision));
string_view shown(value.data(), n); string_view shown(value.data(), n);

@ -92,27 +92,30 @@ class StackArray {
// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns // Calculates `10 * (*v) + carry` and stores the result in `*v` and returns
// the carry. // the carry.
// Requires: `0 <= carry <= 9`
template <typename Int> template <typename Int>
inline Int MultiplyBy10WithCarry(Int *v, Int carry) { inline char MultiplyBy10WithCarry(Int* v, char carry) {
using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>;
BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; BiggerInt tmp =
10 * static_cast<BiggerInt>(*v) + static_cast<BiggerInt>(carry);
*v = static_cast<Int>(tmp); *v = static_cast<Int>(tmp);
return static_cast<Int>(tmp >> (sizeof(Int) * 8)); return static_cast<char>(tmp >> (sizeof(Int) * 8));
} }
// Calculates `(2^64 * carry + *v) / 10`. // Calculates `(2^64 * carry + *v) / 10`.
// Stores the quotient in `*v` and returns the remainder. // Stores the quotient in `*v` and returns the remainder.
// Requires: `0 <= carry <= 9` // Requires: `0 <= carry <= 9`
inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) { inline char DivideBy10WithCarry(uint64_t* v, char carry) {
constexpr uint64_t divisor = 10; constexpr uint64_t divisor = 10;
// 2^64 / divisor = chunk_quotient + chunk_remainder / divisor // 2^64 / divisor = chunk_quotient + chunk_remainder / divisor
constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2); constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2);
constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor; constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor;
const uint64_t carry_u64 = static_cast<uint64_t>(carry);
const uint64_t mod = *v % divisor; const uint64_t mod = *v % divisor;
const uint64_t next_carry = chunk_remainder * carry + mod; const uint64_t next_carry = chunk_remainder * carry_u64 + mod;
*v = *v / divisor + carry * chunk_quotient + next_carry / divisor; *v = *v / divisor + carry_u64 * chunk_quotient + next_carry / divisor;
return next_carry % divisor; return static_cast<char>(next_carry % divisor);
} }
using MaxFloatType = using MaxFloatType =
@ -125,11 +128,11 @@ using MaxFloatType =
// //
// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`. // Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`.
class BinaryToDecimal { class BinaryToDecimal {
static constexpr int ChunksNeeded(int exp) { static constexpr size_t ChunksNeeded(int exp) {
// We will left shift a uint128 by `exp` bits, so we need `128+exp` total // We will left shift a uint128 by `exp` bits, so we need `128+exp` total
// bits. Round up to 32. // bits. Round up to 32.
// See constructor for details about adding `10%` to the value. // See constructor for details about adding `10%` to the value.
return (128 + exp + 31) / 32 * 11 / 10; return static_cast<size_t>((128 + exp + 31) / 32 * 11 / 10);
} }
public: public:
@ -140,7 +143,7 @@ class BinaryToDecimal {
assert(exp > 0); assert(exp > 0);
assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent); assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent);
static_assert( static_assert(
static_cast<int>(StackArray::kMaxCapacity) >= StackArray::kMaxCapacity >=
ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent), ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent),
""); "");
@ -149,9 +152,9 @@ class BinaryToDecimal {
[=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); }); [=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); });
} }
int TotalDigits() const { size_t TotalDigits() const {
return static_cast<int>((decimal_end_ - decimal_start_) * kDigitsPerChunk + return (decimal_end_ - decimal_start_) * kDigitsPerChunk +
CurrentDigits().size()); CurrentDigits().size();
} }
// See the current block of digits. // See the current block of digits.
@ -190,30 +193,31 @@ class BinaryToDecimal {
// the decimal representation is around 7% less efficient in space than the // the decimal representation is around 7% less efficient in space than the
// binary one. We allocate an extra 10% memory to account for this. See // binary one. We allocate an extra 10% memory to account for this. See
// ChunksNeeded for this calculation. // ChunksNeeded for this calculation.
int chunk_index = exp / 32; size_t after_chunk_index = static_cast<size_t>(exp / 32 + 1);
decimal_start_ = decimal_end_ = ChunksNeeded(exp); decimal_start_ = decimal_end_ = ChunksNeeded(exp);
const int offset = exp % 32; const int offset = exp % 32;
// Left shift v by exp bits. // Left shift v by exp bits.
data_[chunk_index] = static_cast<uint32_t>(v << offset); data_[after_chunk_index - 1] = static_cast<uint32_t>(v << offset);
for (v >>= (32 - offset); v; v >>= 32) for (v >>= (32 - offset); v; v >>= 32)
data_[++chunk_index] = static_cast<uint32_t>(v); data_[++after_chunk_index - 1] = static_cast<uint32_t>(v);
while (chunk_index >= 0) { while (after_chunk_index > 0) {
// While we have more than one chunk available, go in steps of 1e9. // While we have more than one chunk available, go in steps of 1e9.
// `data_[chunk_index]` holds the highest non-zero binary chunk, so keep // `data_[after_chunk_index - 1]` holds the highest non-zero binary chunk,
// the variable updated. // so keep the variable updated.
uint32_t carry = 0; uint32_t carry = 0;
for (int i = chunk_index; i >= 0; --i) { for (size_t i = after_chunk_index; i > 0; --i) {
uint64_t tmp = uint64_t{data_[i]} + (uint64_t{carry} << 32); uint64_t tmp = uint64_t{data_[i - 1]} + (uint64_t{carry} << 32);
data_[i] = static_cast<uint32_t>(tmp / uint64_t{1000000000}); data_[i - 1] = static_cast<uint32_t>(tmp / uint64_t{1000000000});
carry = static_cast<uint32_t>(tmp % uint64_t{1000000000}); carry = static_cast<uint32_t>(tmp % uint64_t{1000000000});
} }
// If the highest chunk is now empty, remove it from view. // If the highest chunk is now empty, remove it from view.
if (data_[chunk_index] == 0) --chunk_index; if (data_[after_chunk_index - 1] == 0)
--after_chunk_index;
--decimal_start_; --decimal_start_;
assert(decimal_start_ != chunk_index); assert(decimal_start_ != after_chunk_index - 1);
data_[decimal_start_] = carry; data_[decimal_start_] = carry;
} }
@ -225,13 +229,13 @@ class BinaryToDecimal {
} }
private: private:
static constexpr int kDigitsPerChunk = 9; static constexpr size_t kDigitsPerChunk = 9;
int decimal_start_; size_t decimal_start_;
int decimal_end_; size_t decimal_end_;
char digits_[kDigitsPerChunk]; char digits_[kDigitsPerChunk];
int size_ = 0; size_t size_ = 0;
absl::Span<uint32_t> data_; absl::Span<uint32_t> data_;
}; };
@ -251,25 +255,26 @@ class FractionalDigitGenerator {
static_assert(StackArray::kMaxCapacity >= static_assert(StackArray::kMaxCapacity >=
(Limits::digits + 128 - Limits::min_exponent + 31) / 32, (Limits::digits + 128 - Limits::min_exponent + 31) / 32,
""); "");
StackArray::RunWithCapacity((Limits::digits + exp + 31) / 32, StackArray::RunWithCapacity(
[=](absl::Span<uint32_t> input) { static_cast<size_t>((Limits::digits + exp + 31) / 32),
f(FractionalDigitGenerator(input, v, exp)); [=](absl::Span<uint32_t> input) {
}); f(FractionalDigitGenerator(input, v, exp));
});
} }
// Returns true if there are any more non-zero digits left. // Returns true if there are any more non-zero digits left.
bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } bool HasMoreDigits() const { return next_digit_ != 0 || after_chunk_index_; }
// Returns true if the remainder digits are greater than 5000... // Returns true if the remainder digits are greater than 5000...
bool IsGreaterThanHalf() const { bool IsGreaterThanHalf() const {
return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); return next_digit_ > 5 || (next_digit_ == 5 && after_chunk_index_);
} }
// Returns true if the remainder digits are exactly 5000... // Returns true if the remainder digits are exactly 5000...
bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } bool IsExactlyHalf() const { return next_digit_ == 5 && !after_chunk_index_; }
struct Digits { struct Digits {
int digit_before_nine; char digit_before_nine;
int num_nines; size_t num_nines;
}; };
// Get the next set of digits. // Get the next set of digits.
@ -288,35 +293,37 @@ class FractionalDigitGenerator {
private: private:
// Return the next digit. // Return the next digit.
int GetOneDigit() { char GetOneDigit() {
if (chunk_index_ < 0) return 0; if (!after_chunk_index_)
return 0;
uint32_t carry = 0; char carry = 0;
for (int i = chunk_index_; i >= 0; --i) { for (size_t i = after_chunk_index_; i > 0; --i) {
carry = MultiplyBy10WithCarry(&data_[i], carry); carry = MultiplyBy10WithCarry(&data_[i - 1], carry);
} }
// If the lowest chunk is now empty, remove it from view. // If the lowest chunk is now empty, remove it from view.
if (data_[chunk_index_] == 0) --chunk_index_; if (data_[after_chunk_index_ - 1] == 0)
--after_chunk_index_;
return carry; return carry;
} }
FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp) FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp)
: chunk_index_(exp / 32), data_(data) { : after_chunk_index_(static_cast<size_t>(exp / 32 + 1)), data_(data) {
const int offset = exp % 32; const int offset = exp % 32;
// Right shift `v` by `exp` bits. // Right shift `v` by `exp` bits.
data_[chunk_index_] = static_cast<uint32_t>(v << (32 - offset)); data_[after_chunk_index_ - 1] = static_cast<uint32_t>(v << (32 - offset));
v >>= offset; v >>= offset;
// Make sure we don't overflow the data. We already calculated that // Make sure we don't overflow the data. We already calculated that
// non-zero bits fit, so we might not have space for leading zero bits. // non-zero bits fit, so we might not have space for leading zero bits.
for (int pos = chunk_index_; v; v >>= 32) for (size_t pos = after_chunk_index_ - 1; v; v >>= 32)
data_[--pos] = static_cast<uint32_t>(v); data_[--pos] = static_cast<uint32_t>(v);
// Fill next_digit_, as GetDigits expects it to be populated always. // Fill next_digit_, as GetDigits expects it to be populated always.
next_digit_ = GetOneDigit(); next_digit_ = GetOneDigit();
} }
int next_digit_; char next_digit_;
int chunk_index_; size_t after_chunk_index_;
absl::Span<uint32_t> data_; absl::Span<uint32_t> data_;
}; };
@ -362,7 +369,7 @@ char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) {
auto low = static_cast<uint64_t>(v); auto low = static_cast<uint64_t>(v);
while (high != 0) { while (high != 0) {
uint64_t carry = DivideBy10WithCarry(&high, 0); char carry = DivideBy10WithCarry(&high, 0);
carry = DivideBy10WithCarry(&low, carry); carry = DivideBy10WithCarry(&low, carry);
*--p = carry + '0'; *--p = carry + '0';
} }
@ -373,13 +380,15 @@ char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) {
// shifting. // shifting.
// Performs rounding if necessary to fit within `precision`. // Performs rounding if necessary to fit within `precision`.
// Returns the pointer to one after the last character written. // Returns the pointer to one after the last character written.
char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, char* PrintFractionalDigitsFast(uint64_t v,
int precision) { char* start,
int exp,
size_t precision) {
char *p = start; char *p = start;
v <<= (64 - exp); v <<= (64 - exp);
while (precision > 0) { while (precision > 0) {
if (!v) return p; if (!v) return p;
*p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; *p++ = MultiplyBy10WithCarry(&v, 0) + '0';
--precision; --precision;
} }
@ -393,8 +402,6 @@ char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp,
RoundToEven(p - 1); RoundToEven(p - 1);
} }
assert(precision == 0);
// Precision can only be zero here.
return p; return p;
} }
@ -402,8 +409,10 @@ char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp,
// after shifting. // after shifting.
// Performs rounding if necessary to fit within `precision`. // Performs rounding if necessary to fit within `precision`.
// Returns the pointer to one after the last character written. // Returns the pointer to one after the last character written.
char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, char* PrintFractionalDigitsFast(uint128 v,
int precision) { char* start,
int exp,
size_t precision) {
char *p = start; char *p = start;
v <<= (128 - exp); v <<= (128 - exp);
auto high = static_cast<uint64_t>(v >> 64); auto high = static_cast<uint64_t>(v >> 64);
@ -412,7 +421,7 @@ char *PrintFractionalDigitsFast(uint128 v, char *start, int exp,
// While we have digits to print and `low` is not empty, do the long // While we have digits to print and `low` is not empty, do the long
// multiplication. // multiplication.
while (precision > 0 && low != 0) { while (precision > 0 && low != 0) {
uint64_t carry = MultiplyBy10WithCarry(&low, uint64_t{0}); char carry = MultiplyBy10WithCarry(&low, 0);
carry = MultiplyBy10WithCarry(&high, carry); carry = MultiplyBy10WithCarry(&high, carry);
*p++ = carry + '0'; *p++ = carry + '0';
@ -424,7 +433,7 @@ char *PrintFractionalDigitsFast(uint128 v, char *start, int exp,
// above. // above.
while (precision > 0) { while (precision > 0) {
if (!high) return p; if (!high) return p;
*p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; *p++ = MultiplyBy10WithCarry(&high, 0) + '0';
--precision; --precision;
} }
@ -438,14 +447,12 @@ char *PrintFractionalDigitsFast(uint128 v, char *start, int exp,
RoundToEven(p - 1); RoundToEven(p - 1);
} }
assert(precision == 0);
// Precision can only be zero here.
return p; return p;
} }
struct FormatState { struct FormatState {
char sign_char; char sign_char;
int precision; size_t precision;
const FormatConversionSpecImpl &conv; const FormatConversionSpecImpl &conv;
FormatSinkImpl *sink; FormatSinkImpl *sink;
@ -455,9 +462,9 @@ struct FormatState {
}; };
struct Padding { struct Padding {
int left_spaces; size_t left_spaces;
int zeros; size_t zeros;
int right_spaces; size_t right_spaces;
}; };
Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) {
@ -465,7 +472,7 @@ Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) {
static_cast<size_t>(state.conv.width()) <= total_size) { static_cast<size_t>(state.conv.width()) <= total_size) {
return {0, 0, 0}; return {0, 0, 0};
} }
int missing_chars = state.conv.width() - total_size; size_t missing_chars = static_cast<size_t>(state.conv.width()) - total_size;
if (state.conv.has_left_flag()) { if (state.conv.has_left_flag()) {
return {0, 0, missing_chars}; return {0, 0, missing_chars};
} else if (state.conv.has_zero_flag()) { } else if (state.conv.has_zero_flag()) {
@ -475,8 +482,10 @@ Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) {
} }
} }
void FinalPrint(const FormatState &state, absl::string_view data, void FinalPrint(const FormatState& state,
int padding_offset, int trailing_zeros, absl::string_view data,
size_t padding_offset,
size_t trailing_zeros,
absl::string_view data_postfix) { absl::string_view data_postfix) {
if (state.conv.width() < 0) { if (state.conv.width() < 0) {
// No width specified. Fast-path. // No width specified. Fast-path.
@ -487,10 +496,10 @@ void FinalPrint(const FormatState &state, absl::string_view data,
return; return;
} }
auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + auto padding =
data.size() + data_postfix.size() + ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + data.size() +
static_cast<size_t>(trailing_zeros), data_postfix.size() + trailing_zeros,
state); state);
state.sink->Append(padding.left_spaces, ' '); state.sink->Append(padding.left_spaces, ' ');
if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); if (state.sign_char != '\0') state.sink->Append(1, state.sign_char);
@ -547,15 +556,16 @@ void FormatFFast(Int v, int exp, const FormatState &state) {
if (integral_digits_start[-1] != '0') --integral_digits_start; if (integral_digits_start[-1] != '0') --integral_digits_start;
} }
size_t size = fractional_digits_end - integral_digits_start; size_t size =
static_cast<size_t>(fractional_digits_end - integral_digits_start);
// In `alt` mode (flag #) we keep the `.` even if there are no fractional // In `alt` mode (flag #) we keep the `.` even if there are no fractional
// digits. In non-alt mode, we strip it. // digits. In non-alt mode, we strip it.
if (!state.ShouldPrintDot()) --size; if (!state.ShouldPrintDot()) --size;
FinalPrint(state, absl::string_view(integral_digits_start, size), FinalPrint(state, absl::string_view(integral_digits_start, size),
/*padding_offset=*/0, /*padding_offset=*/0,
static_cast<int>(state.precision - (fractional_digits_end - state.precision - static_cast<size_t>(fractional_digits_end -
fractional_digits_start)), fractional_digits_start),
/*data_postfix=*/""); /*data_postfix=*/"");
} }
@ -567,21 +577,22 @@ void FormatFFast(Int v, int exp, const FormatState &state) {
void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) { void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) {
BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) { BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) {
const size_t total_digits = const size_t total_digits =
btd.TotalDigits() + btd.TotalDigits() + (state.ShouldPrintDot() ? state.precision + 1 : 0);
(state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0);
const auto padding = ExtraWidthToPadding( const auto padding = ExtraWidthToPadding(
total_digits + (state.sign_char != '\0' ? 1 : 0), state); total_digits + (state.sign_char != '\0' ? 1 : 0), state);
state.sink->Append(padding.left_spaces, ' '); state.sink->Append(padding.left_spaces, ' ');
if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); if (state.sign_char != '\0')
state.sink->Append(1, state.sign_char);
state.sink->Append(padding.zeros, '0'); state.sink->Append(padding.zeros, '0');
do { do {
state.sink->Append(btd.CurrentDigits()); state.sink->Append(btd.CurrentDigits());
} while (btd.AdvanceDigits()); } while (btd.AdvanceDigits());
if (state.ShouldPrintDot()) state.sink->Append(1, '.'); if (state.ShouldPrintDot())
state.sink->Append(1, '.');
state.sink->Append(state.precision, '0'); state.sink->Append(state.precision, '0');
state.sink->Append(padding.right_spaces, ' '); state.sink->Append(padding.right_spaces, ' ');
}); });
@ -594,8 +605,7 @@ void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) {
// digits. // digits.
void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) {
const size_t total_digits = const size_t total_digits =
/* 0 */ 1 + /* 0 */ 1 + (state.ShouldPrintDot() ? state.precision + 1 : 0);
(state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0);
auto padding = auto padding =
ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state);
padding.zeros += 1; padding.zeros += 1;
@ -606,7 +616,7 @@ void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) {
if (state.ShouldPrintDot()) state.sink->Append(1, '.'); if (state.ShouldPrintDot()) state.sink->Append(1, '.');
// Print digits // Print digits
int digits_to_go = state.precision; size_t digits_to_go = state.precision;
FractionalDigitGenerator::RunConversion( FractionalDigitGenerator::RunConversion(
v, exp, [&](FractionalDigitGenerator digit_gen) { v, exp, [&](FractionalDigitGenerator digit_gen) {
@ -666,7 +676,8 @@ void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) {
template <typename Int> template <typename Int>
void FormatF(Int mantissa, int exp, const FormatState &state) { void FormatF(Int mantissa, int exp, const FormatState &state) {
if (exp >= 0) { if (exp >= 0) {
const int total_bits = sizeof(Int) * 8 - LeadingZeros(mantissa) + exp; const int total_bits =
static_cast<int>(sizeof(Int) * 8) - LeadingZeros(mantissa) + exp;
// Fallback to the slow stack-based approach if we can't do it in a 64 or // Fallback to the slow stack-based approach if we can't do it in a 64 or
// 128 bit state. // 128 bit state.
@ -686,9 +697,9 @@ void FormatF(Int mantissa, int exp, const FormatState &state) {
// Grab the group of four bits (nibble) from `n`. E.g., nibble 1 corresponds to // Grab the group of four bits (nibble) from `n`. E.g., nibble 1 corresponds to
// bits 4-7. // bits 4-7.
template <typename Int> template <typename Int>
uint8_t GetNibble(Int n, int nibble_index) { uint8_t GetNibble(Int n, size_t nibble_index) {
constexpr Int mask_low_nibble = Int{0xf}; constexpr Int mask_low_nibble = Int{0xf};
int shift = nibble_index * 4; int shift = static_cast<int>(nibble_index * 4);
n &= mask_low_nibble << shift; n &= mask_low_nibble << shift;
return static_cast<uint8_t>((n >> shift) & 0xf); return static_cast<uint8_t>((n >> shift) & 0xf);
} }
@ -696,9 +707,9 @@ uint8_t GetNibble(Int n, int nibble_index) {
// Add one to the given nibble, applying carry to higher nibbles. Returns true // Add one to the given nibble, applying carry to higher nibbles. Returns true
// if overflow, false otherwise. // if overflow, false otherwise.
template <typename Int> template <typename Int>
bool IncrementNibble(int nibble_index, Int *n) { bool IncrementNibble(size_t nibble_index, Int* n) {
constexpr int kShift = sizeof(Int) * 8 - 1; constexpr size_t kShift = sizeof(Int) * 8 - 1;
constexpr int kNumNibbles = sizeof(Int) * 8 / 4; constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4;
Int before = *n >> kShift; Int before = *n >> kShift;
// Here we essentially want to take the number 1 and move it into the requsted // Here we essentially want to take the number 1 and move it into the requsted
// nibble, then add it to *n to effectively increment the nibble. However, // nibble, then add it to *n to effectively increment the nibble. However,
@ -706,28 +717,32 @@ bool IncrementNibble(int nibble_index, Int *n) {
// i.e., if the nibble_index is out of range. So therefore we check for this // i.e., if the nibble_index is out of range. So therefore we check for this
// and if we are out of range we just add 0 which leaves *n unchanged, which // and if we are out of range we just add 0 which leaves *n unchanged, which
// seems like the reasonable thing to do in that case. // seems like the reasonable thing to do in that case.
*n += ((nibble_index >= kNumNibbles) ? 0 : (Int{1} << (nibble_index * 4))); *n += ((nibble_index >= kNumNibbles)
? 0
: (Int{1} << static_cast<int>(nibble_index * 4)));
Int after = *n >> kShift; Int after = *n >> kShift;
return (before && !after) || (nibble_index >= kNumNibbles); return (before && !after) || (nibble_index >= kNumNibbles);
} }
// Return a mask with 1's in the given nibble and all lower nibbles. // Return a mask with 1's in the given nibble and all lower nibbles.
template <typename Int> template <typename Int>
Int MaskUpToNibbleInclusive(int nibble_index) { Int MaskUpToNibbleInclusive(size_t nibble_index) {
constexpr int kNumNibbles = sizeof(Int) * 8 / 4; constexpr size_t kNumNibbles = sizeof(Int) * 8 / 4;
static const Int ones = ~Int{0}; static const Int ones = ~Int{0};
return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1)); ++nibble_index;
return ones >> static_cast<int>(
4 * (std::max(kNumNibbles, nibble_index) - nibble_index));
} }
// Return a mask with 1's below the given nibble. // Return a mask with 1's below the given nibble.
template <typename Int> template <typename Int>
Int MaskUpToNibbleExclusive(int nibble_index) { Int MaskUpToNibbleExclusive(size_t nibble_index) {
return nibble_index <= 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1); return nibble_index == 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1);
} }
template <typename Int> template <typename Int>
Int MoveToNibble(uint8_t nibble, int nibble_index) { Int MoveToNibble(uint8_t nibble, size_t nibble_index) {
return Int{nibble} << (4 * nibble_index); return Int{nibble} << static_cast<int>(4 * nibble_index);
} }
// Given mantissa size, find optimal # of mantissa bits to put in initial digit. // Given mantissa size, find optimal # of mantissa bits to put in initial digit.
@ -744,10 +759,10 @@ Int MoveToNibble(uint8_t nibble, int nibble_index) {
// a multiple of four. Once again, the goal is to have all fractional digits // a multiple of four. Once again, the goal is to have all fractional digits
// represent real precision. // represent real precision.
template <typename Float> template <typename Float>
constexpr int HexFloatLeadingDigitSizeInBits() { constexpr size_t HexFloatLeadingDigitSizeInBits() {
return std::numeric_limits<Float>::digits % 4 > 0 return std::numeric_limits<Float>::digits % 4 > 0
? std::numeric_limits<Float>::digits % 4 ? static_cast<size_t>(std::numeric_limits<Float>::digits % 4)
: 4; : size_t{4};
} }
// This function captures the rounding behavior of glibc for hex float // This function captures the rounding behavior of glibc for hex float
@ -757,16 +772,17 @@ constexpr int HexFloatLeadingDigitSizeInBits() {
// point that is not followed by 800000..., it disregards the parity and rounds // point that is not followed by 800000..., it disregards the parity and rounds
// up if > 8 and rounds down if < 8. // up if > 8 and rounds down if < 8.
template <typename Int> template <typename Int>
bool HexFloatNeedsRoundUp(Int mantissa, int final_nibble_displayed, bool HexFloatNeedsRoundUp(Int mantissa,
size_t final_nibble_displayed,
uint8_t leading) { uint8_t leading) {
// If the last nibble (hex digit) to be displayed is the lowest on in the // If the last nibble (hex digit) to be displayed is the lowest on in the
// mantissa then that means that we don't have any further nibbles to inform // mantissa then that means that we don't have any further nibbles to inform
// rounding, so don't round. // rounding, so don't round.
if (final_nibble_displayed <= 0) { if (final_nibble_displayed == 0) {
return false; return false;
} }
int rounding_nibble_idx = final_nibble_displayed - 1; size_t rounding_nibble_idx = final_nibble_displayed - 1;
constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; constexpr size_t kTotalNibbles = sizeof(Int) * 8 / 4;
assert(final_nibble_displayed <= kTotalNibbles); assert(final_nibble_displayed <= kTotalNibbles);
Int mantissa_up_to_rounding_nibble_inclusive = Int mantissa_up_to_rounding_nibble_inclusive =
mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx); mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx);
@ -793,7 +809,7 @@ struct HexFloatTypeParams {
} }
int min_exponent; int min_exponent;
int leading_digit_size_bits; size_t leading_digit_size_bits;
}; };
// Hex Float Rounding. First check if we need to round; if so, then we do that // Hex Float Rounding. First check if we need to round; if so, then we do that
@ -803,10 +819,12 @@ struct HexFloatTypeParams {
template <typename Int> template <typename Int>
void FormatARound(bool precision_specified, const FormatState &state, void FormatARound(bool precision_specified, const FormatState &state,
uint8_t *leading, Int *mantissa, int *exp) { uint8_t *leading, Int *mantissa, int *exp) {
constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; constexpr size_t kTotalNibbles = sizeof(Int) * 8 / 4;
// Index of the last nibble that we could display given precision. // Index of the last nibble that we could display given precision.
int final_nibble_displayed = size_t final_nibble_displayed =
precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0; precision_specified
? (std::max(kTotalNibbles, state.precision) - state.precision)
: 0;
if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) { if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) {
// Need to round up. // Need to round up.
bool overflow = IncrementNibble(final_nibble_displayed, mantissa); bool overflow = IncrementNibble(final_nibble_displayed, mantissa);
@ -830,9 +848,9 @@ void FormatARound(bool precision_specified, const FormatState &state,
template <typename Int> template <typename Int>
void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading,
Int *mantissa, int *exp) { Int *mantissa, int *exp) {
constexpr int kIntBits = sizeof(Int) * 8; constexpr size_t kIntBits = sizeof(Int) * 8;
static const Int kHighIntBit = Int{1} << (kIntBits - 1); static const Int kHighIntBit = Int{1} << (kIntBits - 1);
const int kLeadDigitBitsCount = float_traits.leading_digit_size_bits; const size_t kLeadDigitBitsCount = float_traits.leading_digit_size_bits;
// Normalize mantissa so that highest bit set is in MSB position, unless we // Normalize mantissa so that highest bit set is in MSB position, unless we
// get interrupted by the exponent threshold. // get interrupted by the exponent threshold.
while (*mantissa && !(*mantissa & kHighIntBit)) { while (*mantissa && !(*mantissa & kHighIntBit)) {
@ -846,18 +864,18 @@ void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading,
} }
// Extract bits for leading digit then shift them away leaving the // Extract bits for leading digit then shift them away leaving the
// fractional part. // fractional part.
*leading = *leading = static_cast<uint8_t>(
static_cast<uint8_t>(*mantissa >> (kIntBits - kLeadDigitBitsCount)); *mantissa >> static_cast<int>(kIntBits - kLeadDigitBitsCount));
*exp -= (*mantissa != 0) ? kLeadDigitBitsCount : *exp; *exp -= (*mantissa != 0) ? static_cast<int>(kLeadDigitBitsCount) : *exp;
*mantissa <<= kLeadDigitBitsCount; *mantissa <<= static_cast<int>(kLeadDigitBitsCount);
} }
template <typename Int> template <typename Int>
void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp, void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp,
bool uppercase, const FormatState &state) { bool uppercase, const FormatState &state) {
// Int properties. // Int properties.
constexpr int kIntBits = sizeof(Int) * 8; constexpr size_t kIntBits = sizeof(Int) * 8;
constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; constexpr size_t kTotalNibbles = sizeof(Int) * 8 / 4;
// Did the user specify a precision explicitly? // Did the user specify a precision explicitly?
const bool precision_specified = state.conv.precision() >= 0; const bool precision_specified = state.conv.precision() >= 0;
@ -903,16 +921,19 @@ void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp,
} }
// ============ Fractional Digits ============ // ============ Fractional Digits ============
int digits_emitted = 0; size_t digits_emitted = 0;
while (mantissa > 0) { while (mantissa > 0) {
*digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)]; *digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)];
mantissa <<= 4; mantissa <<= 4;
++digits_emitted; ++digits_emitted;
} }
int trailing_zeros = size_t trailing_zeros = 0;
precision_specified ? state.precision - digits_emitted : 0; if (precision_specified) {
assert(trailing_zeros >= 0); assert(state.precision >= digits_emitted);
auto digits_result = string_view(digits_buffer, digits_iter - digits_buffer); trailing_zeros = state.precision - digits_emitted;
}
auto digits_result = string_view(
digits_buffer, static_cast<size_t>(digits_iter - digits_buffer));
// =============== Exponent ================== // =============== Exponent ==================
constexpr size_t kBufSizeForExpDecRepr = constexpr size_t kBufSizeForExpDecRepr =
@ -925,11 +946,11 @@ void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp,
numbers_internal::FastIntToBuffer(exp < 0 ? -exp : exp, exp_buffer + 2); numbers_internal::FastIntToBuffer(exp < 0 ? -exp : exp, exp_buffer + 2);
// ============ Assemble Result ============== // ============ Assemble Result ==============
FinalPrint(state, // FinalPrint(state,
digits_result, // 0xN.NNN... digits_result, // 0xN.NNN...
2, // offset in `data` to start padding if needed. 2, // offset of any padding
trailing_zeros, // num remaining mantissa padding zeros static_cast<size_t>(trailing_zeros), // remaining mantissa padding
exp_buffer); // exponent exp_buffer); // exponent
} }
char *CopyStringTo(absl::string_view v, char *out) { char *CopyStringTo(absl::string_view v, char *out) {
@ -961,10 +982,10 @@ bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv,
int n = snprintf(&space[0], space.size(), fmt, w, p, v); int n = snprintf(&space[0], space.size(), fmt, w, p, v);
if (n < 0) return false; if (n < 0) return false;
if (static_cast<size_t>(n) < space.size()) { if (static_cast<size_t>(n) < space.size()) {
result = absl::string_view(space.data(), n); result = absl::string_view(space.data(), static_cast<size_t>(n));
break; break;
} }
space.resize(n + 1); space.resize(static_cast<size_t>(n) + 1);
} }
sink->Append(result); sink->Append(result);
return true; return true;
@ -972,13 +993,13 @@ bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv,
// 128-bits in decimal: ceil(128*log(2)/log(10)) // 128-bits in decimal: ceil(128*log(2)/log(10))
// or std::numeric_limits<__uint128_t>::digits10 // or std::numeric_limits<__uint128_t>::digits10
constexpr int kMaxFixedPrecision = 39; constexpr size_t kMaxFixedPrecision = 39;
constexpr int kBufferLength = /*sign*/ 1 + constexpr size_t kBufferLength = /*sign*/ 1 +
/*integer*/ kMaxFixedPrecision + /*integer*/ kMaxFixedPrecision +
/*point*/ 1 + /*point*/ 1 +
/*fraction*/ kMaxFixedPrecision + /*fraction*/ kMaxFixedPrecision +
/*exponent e+123*/ 5; /*exponent e+123*/ 5;
struct Buffer { struct Buffer {
void push_front(char c) { void push_front(char c) {
@ -1001,7 +1022,7 @@ struct Buffer {
char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; } char last_digit() const { return end[-1] == '.' ? end[-2] : end[-1]; }
int size() const { return static_cast<int>(end - begin); } size_t size() const { return static_cast<size_t>(end - begin); }
char data[kBufferLength]; char data[kBufferLength];
char *begin; char *begin;
@ -1030,8 +1051,9 @@ bool ConvertNonNumericFloats(char sign_char, Float v,
return false; return false;
} }
return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, return sink->PutPaddedString(
conv.has_left_flag()); string_view(text, static_cast<size_t>(ptr - text)), conv.width(), -1,
conv.has_left_flag());
} }
// Round up the last digit of the value. // Round up the last digit of the value.
@ -1068,11 +1090,11 @@ void PrintExponent(int exp, char e, Buffer *out) {
} }
// Exponent digits. // Exponent digits.
if (exp > 99) { if (exp > 99) {
out->push_back(exp / 100 + '0'); out->push_back(static_cast<char>(exp / 100) + '0');
out->push_back(exp / 10 % 10 + '0'); out->push_back(exp / 10 % 10 + '0');
out->push_back(exp % 10 + '0'); out->push_back(exp % 10 + '0');
} else { } else {
out->push_back(exp / 10 + '0'); out->push_back(static_cast<char>(exp / 10) + '0');
out->push_back(exp % 10 + '0'); out->push_back(exp % 10 + '0');
} }
} }
@ -1115,8 +1137,8 @@ Decomposed<Float> Decompose(Float v) {
// In Fixed mode, we add a '.' at the end. // In Fixed mode, we add a '.' at the end.
// In Precision mode, we add a '.' after the first digit. // In Precision mode, we add a '.' after the first digit.
template <FormatStyle mode, typename Int> template <FormatStyle mode, typename Int>
int PrintIntegralDigits(Int digits, Buffer *out) { size_t PrintIntegralDigits(Int digits, Buffer* out) {
int printed = 0; size_t printed = 0;
if (digits) { if (digits) {
for (; digits; digits /= 10) out->push_front(digits % 10 + '0'); for (; digits; digits /= 10) out->push_front(digits % 10 + '0');
printed = out->size(); printed = out->size();
@ -1135,10 +1157,10 @@ int PrintIntegralDigits(Int digits, Buffer *out) {
} }
// Back out 'extra_digits' digits and round up if necessary. // Back out 'extra_digits' digits and round up if necessary.
bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value, void RemoveExtraPrecision(size_t extra_digits,
Buffer *out, int *exp_out) { bool has_leftover_value,
if (extra_digits <= 0) return false; Buffer* out,
int* exp_out) {
// Back out the extra digits // Back out the extra digits
out->end -= extra_digits; out->end -= extra_digits;
@ -1158,15 +1180,17 @@ bool RemoveExtraPrecision(int extra_digits, bool has_leftover_value,
if (needs_to_round_up) { if (needs_to_round_up) {
RoundUp<FormatStyle::Precision>(out, exp_out); RoundUp<FormatStyle::Precision>(out, exp_out);
} }
return true;
} }
// Print the value into the buffer. // Print the value into the buffer.
// This will not include the exponent, which will be returned in 'exp_out' for // This will not include the exponent, which will be returned in 'exp_out' for
// Precision mode. // Precision mode.
template <typename Int, typename Float, FormatStyle mode> template <typename Int, typename Float, FormatStyle mode>
bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out, bool FloatToBufferImpl(Int int_mantissa,
int *exp_out) { int exp,
size_t precision,
Buffer* out,
int* exp_out) {
assert((CanFitMantissa<Float, Int>())); assert((CanFitMantissa<Float, Int>()));
const int int_bits = std::numeric_limits<Int>::digits; const int int_bits = std::numeric_limits<Int>::digits;
@ -1182,14 +1206,16 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
// The value will overflow the Int // The value will overflow the Int
return false; return false;
} }
int digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out); size_t digits_printed = PrintIntegralDigits<mode>(int_mantissa << exp, out);
int digits_to_zero_pad = precision; size_t digits_to_zero_pad = precision;
if (mode == FormatStyle::Precision) { if (mode == FormatStyle::Precision) {
*exp_out = digits_printed - 1; *exp_out = static_cast<int>(digits_printed - 1);
digits_to_zero_pad -= digits_printed - 1; if (digits_to_zero_pad < digits_printed - 1) {
if (RemoveExtraPrecision(-digits_to_zero_pad, false, out, exp_out)) { RemoveExtraPrecision(digits_printed - 1 - digits_to_zero_pad, false,
out, exp_out);
return true; return true;
} }
digits_to_zero_pad -= digits_printed - 1;
} }
for (; digits_to_zero_pad-- > 0;) out->push_back('0'); for (; digits_to_zero_pad-- > 0;) out->push_back('0');
return true; return true;
@ -1203,10 +1229,10 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
const Int mask = (Int{1} << exp) - 1; const Int mask = (Int{1} << exp) - 1;
// Print the integral part first. // Print the integral part first.
int digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out); size_t digits_printed = PrintIntegralDigits<mode>(int_mantissa >> exp, out);
int_mantissa &= mask; int_mantissa &= mask;
int fractional_count = precision; size_t fractional_count = precision;
if (mode == FormatStyle::Precision) { if (mode == FormatStyle::Precision) {
if (digits_printed == 0) { if (digits_printed == 0) {
// Find the first non-zero digit, when in Precision mode. // Find the first non-zero digit, when in Precision mode.
@ -1222,20 +1248,21 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
int_mantissa &= mask; int_mantissa &= mask;
} else { } else {
// We already have a digit, and a '.' // We already have a digit, and a '.'
*exp_out = digits_printed - 1; *exp_out = static_cast<int>(digits_printed - 1);
fractional_count -= *exp_out; if (fractional_count < digits_printed - 1) {
if (RemoveExtraPrecision(-fractional_count, int_mantissa != 0, out,
exp_out)) {
// If we had enough digits, return right away. // If we had enough digits, return right away.
// The code below will try to round again otherwise. // The code below will try to round again otherwise.
RemoveExtraPrecision(digits_printed - 1 - fractional_count,
int_mantissa != 0, out, exp_out);
return true; return true;
} }
fractional_count -= digits_printed - 1;
} }
} }
auto get_next_digit = [&] { auto get_next_digit = [&] {
int_mantissa *= 10; int_mantissa *= 10;
int digit = static_cast<int>(int_mantissa >> exp); char digit = static_cast<char>(int_mantissa >> exp);
int_mantissa &= mask; int_mantissa &= mask;
return digit; return digit;
}; };
@ -1245,7 +1272,7 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
out->push_back(get_next_digit() + '0'); out->push_back(get_next_digit() + '0');
} }
int next_digit = get_next_digit(); char next_digit = get_next_digit();
if (next_digit > 5 || if (next_digit > 5 ||
(next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) { (next_digit == 5 && (int_mantissa || out->last_digit() % 2 == 1))) {
RoundUp<mode>(out, exp_out); RoundUp<mode>(out, exp_out);
@ -1255,24 +1282,25 @@ bool FloatToBufferImpl(Int int_mantissa, int exp, int precision, Buffer *out,
} }
template <FormatStyle mode, typename Float> template <FormatStyle mode, typename Float>
bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, bool FloatToBuffer(Decomposed<Float> decomposed,
int *exp) { size_t precision,
Buffer* out,
int* exp) {
if (precision > kMaxFixedPrecision) return false; if (precision > kMaxFixedPrecision) return false;
// Try with uint64_t. // Try with uint64_t.
if (CanFitMantissa<Float, std::uint64_t>() && if (CanFitMantissa<Float, std::uint64_t>() &&
FloatToBufferImpl<std::uint64_t, Float, mode>( FloatToBufferImpl<std::uint64_t, Float, mode>(
static_cast<std::uint64_t>(decomposed.mantissa), static_cast<std::uint64_t>(decomposed.mantissa), decomposed.exponent,
static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp)) precision, out, exp))
return true; return true;
#if defined(ABSL_HAVE_INTRINSIC_INT128) #if defined(ABSL_HAVE_INTRINSIC_INT128)
// If that is not enough, try with __uint128_t. // If that is not enough, try with __uint128_t.
return CanFitMantissa<Float, __uint128_t>() && return CanFitMantissa<Float, __uint128_t>() &&
FloatToBufferImpl<__uint128_t, Float, mode>( FloatToBufferImpl<__uint128_t, Float, mode>(
static_cast<__uint128_t>(decomposed.mantissa), static_cast<__uint128_t>(decomposed.mantissa), decomposed.exponent,
static_cast<__uint128_t>(decomposed.exponent), precision, out, precision, out, exp);
exp);
#endif #endif
return false; return false;
} }
@ -1280,12 +1308,15 @@ bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out,
void WriteBufferToSink(char sign_char, absl::string_view str, void WriteBufferToSink(char sign_char, absl::string_view str,
const FormatConversionSpecImpl &conv, const FormatConversionSpecImpl &conv,
FormatSinkImpl *sink) { FormatSinkImpl *sink) {
int left_spaces = 0, zeros = 0, right_spaces = 0; size_t left_spaces = 0, zeros = 0, right_spaces = 0;
int missing_chars = size_t missing_chars = 0;
conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) - if (conv.width() >= 0) {
static_cast<int>(sign_char != 0), const size_t conv_width_size_t = static_cast<size_t>(conv.width());
0) const size_t existing_chars =
: 0; str.size() + static_cast<size_t>(sign_char != 0);
if (conv_width_size_t > existing_chars)
missing_chars = conv_width_size_t - existing_chars;
}
if (conv.has_left_flag()) { if (conv.has_left_flag()) {
right_spaces = missing_chars; right_spaces = missing_chars;
} else if (conv.has_zero_flag()) { } else if (conv.has_zero_flag()) {
@ -1321,7 +1352,8 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv,
return true; return true;
} }
int precision = conv.precision() < 0 ? 6 : conv.precision(); size_t precision =
conv.precision() < 0 ? 6 : static_cast<size_t>(conv.precision());
int exp = 0; int exp = 0;
@ -1348,12 +1380,12 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv,
&buffer); &buffer);
} else if (c == FormatConversionCharInternal::g || } else if (c == FormatConversionCharInternal::g ||
c == FormatConversionCharInternal::G) { c == FormatConversionCharInternal::G) {
precision = std::max(0, precision - 1); precision = std::max(precision, size_t{1}) - 1;
if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer,
&exp)) { &exp)) {
return FallbackToSnprintf(v, conv, sink); return FallbackToSnprintf(v, conv, sink);
} }
if (precision + 1 > exp && exp >= -4) { if ((exp < 0 || precision + 1 > static_cast<size_t>(exp)) && exp >= -4) {
if (exp < 0) { if (exp < 0) {
// Have 1.23456, needs 0.00123456 // Have 1.23456, needs 0.00123456
// Move the first digit // Move the first digit
@ -1388,9 +1420,11 @@ bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv,
return false; return false;
} }
WriteBufferToSink(sign_char, WriteBufferToSink(
absl::string_view(buffer.begin, buffer.end - buffer.begin), sign_char,
conv, sink); absl::string_view(buffer.begin,
static_cast<size_t>(buffer.end - buffer.begin)),
conv, sink);
return true; return true;
} }

Loading…
Cancel
Save