Coverage Report

Created: 2025-06-26 14:35

/root/doris/be/src/gutil/strings/numbers.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2010 Google Inc. All Rights Reserved.
2
// Refactored from contributions of various authors in strings/strutil.cc
3
//
4
// This file contains string processing functions related to
5
// numeric values.
6
7
#include "gutil/strings/numbers.h"
8
9
#include <fmt/compile.h>
10
#include <fmt/format.h>
11
12
#include <cfloat>
13
14
#include "absl/strings/ascii.h"
15
#include "butil/macros.h"
16
#include "common/logging.h"
17
18
18.0M
bool safe_strtof(const char* str, float* value) {
19
18.0M
    char* endptr;
20
#ifdef _MSC_VER // has no strtof()
21
    *value = strtod(str, &endptr);
22
#else
23
18.0M
    *value = strtof(str, &endptr);
24
18.0M
#endif
25
18.0M
    if (endptr != str) {
26
18.0M
        while (absl::ascii_isspace(*endptr)) ++endptr;
27
18.0M
    }
28
    // Ignore range errors from strtod/strtof.
29
    // The values it returns on underflow and
30
    // overflow are the right fallback in a
31
    // robust setting.
32
18.0M
    return *str != '\0' && *endptr == '\0';
33
18.0M
}
34
35
0
bool safe_strtod(const char* str, double* value) {
36
0
    char* endptr;
37
0
    *value = strtod(str, &endptr);
38
0
    if (endptr != str) {
39
0
        while (absl::ascii_isspace(*endptr)) ++endptr;
40
0
    }
41
    // Ignore range errors from strtod.  The values it
42
    // returns on underflow and overflow are the right
43
    // fallback in a robust setting.
44
0
    return *str != '\0' && *endptr == '\0';
45
0
}
46
47
0
bool safe_strtof(const std::string& str, float* value) {
48
0
    return safe_strtof(str.c_str(), value);
49
0
}
50
51
0
bool safe_strtod(const std::string& str, double* value) {
52
0
    return safe_strtod(str.c_str(), value);
53
0
}
54
55
// ----------------------------------------------------------------------
56
// SimpleDtoa()
57
// SimpleFtoa()
58
// DoubleToBuffer()
59
// FloatToBuffer()
60
//    We want to print the value without losing precision, but we also do
61
//    not want to print more digits than necessary.  This turns out to be
62
//    trickier than it sounds.  Numbers like 0.2 cannot be represented
63
//    exactly in binary.  If we print 0.2 with a very large precision,
64
//    e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167".
65
//    On the other hand, if we set the precision too low, we lose
66
//    significant digits when printing numbers that actually need them.
67
//    It turns out there is no precision value that does the right thing
68
//    for all numbers.
69
//
70
//    Our strategy is to first try printing with a precision that is never
71
//    over-precise, then parse the result with strtod() to see if it
72
//    matches.  If not, we print again with a precision that will always
73
//    give a precise result, but may use more digits than necessary.
74
//
75
//    An arguably better strategy would be to use the algorithm described
76
//    in "How to Print Floating-Point Numbers Accurately" by Steele &
77
//    White, e.g. as implemented by David M. Gay's dtoa().  It turns out,
78
//    however, that the following implementation is about as fast as
79
//    DMG's code.  Furthermore, DMG's code locks mutexes, which means it
80
//    will not scale well on multi-core machines.  DMG's code is slightly
81
//    more accurate (in that it will never use more digits than
82
//    necessary), but this is probably irrelevant for most users.
83
//
84
//    Rob Pike and Ken Thompson also have an implementation of dtoa() in
85
//    third_party/fmt/fltfmt.cc.  Their implementation is similar to this
86
//    one in that it makes guesses and then uses strtod() to check them.
87
//    Their implementation is faster because they use their own code to
88
//    generate the digits in the first place rather than use snprintf(),
89
//    thus avoiding format string parsing overhead.  However, this makes
90
//    it considerably more complicated than the following implementation,
91
//    and it is embedded in a larger library.  If speed turns out to be
92
//    an issue, we could re-implement this in terms of their
93
//    implementation.
94
// ----------------------------------------------------------------------
95
11
int DoubleToBuffer(double value, int width, char* buffer) {
96
    // DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all
97
    // platforms these days.  Just in case some system exists where DBL_DIG
98
    // is significantly larger -- and risks overflowing our buffer -- we have
99
    // this assert.
100
11
    COMPILE_ASSERT(DBL_DIG < 20, DBL_DIG_is_too_big);
101
102
11
    int snprintf_result = snprintf(buffer, width, "%.*g", DBL_DIG, value);
103
104
    // The snprintf should never overflow because the buffer is significantly
105
    // larger than the precision we asked for.
106
11
    DCHECK(snprintf_result > 0 && snprintf_result < width);
107
108
11
    if (strtod(buffer, nullptr) != value) {
109
3
        snprintf_result = snprintf(buffer, width, "%.*g", DBL_DIG + 2, value);
110
111
        // Should never overflow; see above.
112
3
        DCHECK(snprintf_result > 0 && snprintf_result < width);
113
3
    }
114
115
11
    return snprintf_result;
116
11
}
117
118
18.0M
int FloatToBuffer(float value, int width, char* buffer) {
119
    // FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
120
    // platforms these days.  Just in case some system exists where FLT_DIG
121
    // is significantly larger -- and risks overflowing our buffer -- we have
122
    // this assert.
123
18.0M
    COMPILE_ASSERT(FLT_DIG < 10, FLT_DIG_is_too_big);
124
125
18.0M
    int snprintf_result = snprintf(buffer, width, "%.*g", FLT_DIG, value);
126
127
    // The snprintf should never overflow because the buffer is significantly
128
    // larger than the precision we asked for.
129
18.0M
    DCHECK(snprintf_result > 0 && snprintf_result < width);
130
131
18.0M
    float parsed_value;
132
18.0M
    if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
133
10
        snprintf_result = snprintf(buffer, width, "%.*g", FLT_DIG + 2, value);
134
135
        // Should never overflow; see above.
136
10
        DCHECK(snprintf_result > 0 && snprintf_result < width);
137
10
    }
138
139
18.0M
    return snprintf_result;
140
18.0M
}
141
142
862
int FastDoubleToBuffer(double value, char* buffer) {
143
862
    auto end = fmt::format_to(buffer, FMT_COMPILE("{}"), value);
144
862
    *end = '\0';
145
862
    return end - buffer;
146
862
}
147
148
761
int FastFloatToBuffer(float value, char* buffer) {
149
761
    auto* end = fmt::format_to(buffer, FMT_COMPILE("{}"), value);
150
761
    *end = '\0';
151
761
    return end - buffer;
152
761
}