Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
b3cc9c05
Commit
b3cc9c05
authored
Apr 28, 2019
by
Paul Dreik
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'upstream/master' into invalidcolons
# Conflicts: # test/chrono-test.cc
parents
2e3352fd
4c721e3a
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
107 additions
and
48 deletions
+107
-48
include/fmt/chrono.h
include/fmt/chrono.h
+21
-10
include/fmt/format-inl.h
include/fmt/format-inl.h
+58
-21
include/fmt/format.h
include/fmt/format.h
+20
-11
src/format.cc
src/format.cc
+3
-3
test/chrono-test.cc
test/chrono-test.cc
+1
-0
test/format-impl-test.cc
test/format-impl-test.cc
+2
-2
test/format-test.cc
test/format-test.cc
+2
-1
No files found.
include/fmt/chrono.h
View file @
b3cc9c05
...
...
@@ -585,18 +585,20 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
public:
formatter
()
:
spec
(),
precision
(
-
1
)
{}
typedef
typename
basic_parse_context
<
Char
>::
iterator
iterator
;
struct
parse_range
{
iterator
begin
;
iterator
end
;
};
FMT_CONSTEXPR
auto
parse
(
basic_parse_context
<
Char
>&
ctx
)
->
decltype
(
ctx
.
begin
())
{
FMT_CONSTEXPR
parse_range
do_parse
(
basic_parse_context
<
Char
>&
ctx
)
{
auto
begin
=
ctx
.
begin
(),
end
=
ctx
.
end
();
if
(
begin
==
end
)
return
begin
;
if
(
begin
==
end
)
return
{
begin
,
end
}
;
spec_handler
handler
{
*
this
,
ctx
,
format_str
};
begin
=
internal
::
parse_align
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
if
(
begin
==
end
)
return
{
begin
,
end
}
;
begin
=
internal
::
parse_width
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
if
(
begin
==
end
)
return
{
begin
,
end
}
;
if
(
*
begin
==
'.'
)
{
if
(
std
::
is_floating_point
<
Rep
>::
value
)
begin
=
internal
::
parse_precision
(
begin
,
end
,
handler
);
...
...
@@ -604,9 +606,18 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
handler
.
on_error
(
"precision not allowed for this argument type"
);
}
end
=
parse_chrono_format
(
begin
,
end
,
internal
::
chrono_format_checker
());
format_str
=
basic_string_view
<
Char
>
(
&*
begin
,
internal
::
to_unsigned
(
end
-
begin
));
return
end
;
return
{
begin
,
end
};
}
public:
formatter
()
:
spec
(),
precision
(
-
1
)
{}
FMT_CONSTEXPR
auto
parse
(
basic_parse_context
<
Char
>&
ctx
)
->
decltype
(
ctx
.
begin
())
{
auto
range
=
do_parse
(
ctx
);
format_str
=
basic_string_view
<
Char
>
(
&*
range
.
begin
,
internal
::
to_unsigned
(
range
.
end
-
range
.
begin
));
return
range
.
end
;
}
template
<
typename
FormatContext
>
...
...
include/fmt/format-inl.h
View file @
b3cc9c05
...
...
@@ -494,10 +494,12 @@ enum result {
};
}
// Generates output using Grisu2 digit-gen algorithm.
// Generates output using the Grisu digit-gen algorithm.
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
template
<
typename
Handler
>
digits
::
result
grisu
2
_gen_digits
(
fp
value
,
uint64_t
error
,
int
&
exp
,
Handler
&
handler
)
{
digits
::
result
grisu_gen_digits
(
fp
value
,
uint64_t
error
,
int
&
exp
,
Handler
&
handler
)
{
fp
one
(
1ull
<<
-
value
.
e
,
value
.
e
);
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
// zero because it contains a product of two 64-bit numbers with MSB set (due
...
...
@@ -635,34 +637,57 @@ struct fixed_handler {
};
// The shortest representation digit handler.
struct
shortest_handler
{
template
<
int
GRISU_VERSION
>
struct
grisu_
shortest_handler
{
char
*
buf
;
int
size
;
fp
diff
;
// wp_w in Grisu.
// Distance between scaled value and upper bound (wp_W in Grisu3).
uint64_t
diff
;
digits
::
result
on_start
(
uint64_t
,
uint64_t
,
uint64_t
,
int
&
)
{
return
digits
::
more
;
}
// This implements Grisu3's round_weed.
digits
::
result
on_digit
(
char
digit
,
uint64_t
divisor
,
uint64_t
remainder
,
uint64_t
error
,
int
exp
,
bool
integral
)
{
buf
[
size
++
]
=
digit
;
if
(
remainder
>
error
)
return
digits
::
more
;
uint64_t
d
=
integral
?
diff
.
f
:
diff
.
f
*
data
::
POWERS_OF_10_64
[
-
exp
];
while
(
remainder
<
d
&&
error
-
remainder
>=
divisor
&&
(
remainder
+
divisor
<
d
||
d
-
remainder
>
remainder
+
divisor
-
d
))
{
if
(
remainder
>=
error
)
return
digits
::
more
;
if
(
GRISU_VERSION
!=
3
)
{
uint64_t
d
=
integral
?
diff
:
diff
*
data
::
POWERS_OF_10_64
[
-
exp
];
while
(
remainder
<
d
&&
error
-
remainder
>=
divisor
&&
(
remainder
+
divisor
<
d
||
d
-
remainder
>
remainder
+
divisor
-
d
))
{
--
buf
[
size
-
1
];
remainder
+=
divisor
;
}
return
digits
::
done
;
}
uint64_t
unit
=
integral
?
1
:
data
::
POWERS_OF_10_64
[
-
exp
];
uint64_t
up
=
diff
+
unit
;
// wp_Wup
while
(
remainder
<
up
&&
error
-
remainder
>=
divisor
&&
(
remainder
+
divisor
<
up
||
up
-
remainder
>
remainder
+
divisor
-
up
))
{
--
buf
[
size
-
1
];
remainder
+=
divisor
;
}
return
digits
::
done
;
uint64_t
down
=
diff
-
unit
;
// wp_Wdown
if
(
remainder
<
down
&&
error
-
remainder
>=
divisor
&&
(
remainder
+
divisor
<
down
||
down
-
remainder
>
remainder
+
divisor
-
down
))
{
return
digits
::
error
;
}
return
2
*
unit
<=
remainder
&&
remainder
<=
error
-
4
*
unit
?
digits
::
done
:
digits
::
error
;
}
};
template
<
typename
Double
,
typename
std
::
enable_if
<
sizeof
(
Double
)
==
sizeof
(
uint64_t
),
int
>
::
type
>
FMT_FUNC
bool
grisu
2
_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
bool
fixed
,
int
&
exp
)
{
FMT_FUNC
bool
grisu_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
unsigned
options
,
int
&
exp
)
{
FMT_ASSERT
(
value
>=
0
,
"value is negative"
);
bool
fixed
=
(
options
&
grisu_options
::
fixed
)
!=
0
;
if
(
value
<=
0
)
{
// <= instead of == to silence a warning.
if
(
precision
<
0
||
!
fixed
)
{
exp
=
0
;
...
...
@@ -685,7 +710,7 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
min_exp
-
(
fp_value
.
e
+
fp
::
significand_size
),
cached_exp10
);
fp_value
=
fp_value
*
cached_pow
;
fixed_handler
handler
{
buf
.
data
(),
0
,
precision
,
-
cached_exp10
,
fixed
};
if
(
grisu
2
_gen_digits
(
fp_value
,
1
,
exp
,
handler
)
==
digits
::
error
)
if
(
grisu_gen_digits
(
fp_value
,
1
,
exp
,
handler
)
==
digits
::
error
)
return
false
;
buf
.
resize
(
to_unsigned
(
handler
.
size
));
}
else
{
...
...
@@ -695,17 +720,29 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
// the exponent in the range [min_exp, -32].
auto
cached_pow
=
get_cached_power
(
// \tilde{c}_{-k} in Grisu.
min_exp
-
(
upper
.
e
+
fp
::
significand_size
),
cached_exp10
);
upper
=
upper
*
cached_pow
;
// \tilde{M}^+ in Grisu.
--
upper
.
f
;
// \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
assert
(
min_exp
<=
upper
.
e
&&
upper
.
e
<=
-
32
);
fp_value
.
normalize
();
fp_value
=
fp_value
*
cached_pow
;
lower
=
lower
*
cached_pow
;
// \tilde{M}^- in Grisu.
++
lower
.
f
;
// \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
shortest_handler
handler
{
buf
.
data
(),
0
,
upper
-
fp_value
};
auto
result
=
grisu2_gen_digits
(
upper
,
upper
.
f
-
lower
.
f
,
exp
,
handler
);
upper
=
upper
*
cached_pow
;
// \tilde{M}^+ in Grisu.
assert
(
min_exp
<=
upper
.
e
&&
upper
.
e
<=
-
32
);
auto
result
=
digits
::
result
();
int
size
=
0
;
if
((
options
&
grisu_options
::
grisu3
)
!=
0
)
{
--
lower
.
f
;
// \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
++
upper
.
f
;
// \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
// Numbers outside of (lower, upper) definitely do not round to value.
grisu_shortest_handler
<
3
>
handler
{
buf
.
data
(),
0
,
(
upper
-
fp_value
).
f
};
result
=
grisu_gen_digits
(
upper
,
upper
.
f
-
lower
.
f
,
exp
,
handler
);
size
=
handler
.
size
;
}
else
{
++
lower
.
f
;
// \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
--
upper
.
f
;
// \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
grisu_shortest_handler
<
2
>
handler
{
buf
.
data
(),
0
,
(
upper
-
fp_value
).
f
};
result
=
grisu_gen_digits
(
upper
,
upper
.
f
-
lower
.
f
,
exp
,
handler
);
size
=
handler
.
size
;
}
if
(
result
==
digits
::
error
)
return
false
;
buf
.
resize
(
to_unsigned
(
handler
.
size
));
buf
.
resize
(
to_unsigned
(
size
));
}
exp
-=
cached_exp10
;
return
true
;
...
...
include/fmt/format.h
View file @
b3cc9c05
...
...
@@ -1124,13 +1124,19 @@ FMT_CONSTEXPR unsigned basic_parse_context<Char, ErrorHandler>::next_arg_id() {
namespace
internal
{
// Formats value using Grisu2 algorithm:
namespace
grisu_options
{
enum
{
fixed
=
1
,
grisu3
=
2
};
}
// Formats value using the Grisu algorithm:
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
==
sizeof
(
uint64_t
))>
FMT_API
bool
grisu2_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
bool
fixed
,
int
&
exp
);
FMT_API
bool
grisu_format
(
Double
,
buffer
<
char
>&
,
int
,
unsigned
,
int
&
);
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
!=
sizeof
(
uint64_t
))>
inline
bool
grisu
2_format
(
Double
,
buffer
<
char
>&
,
int
,
bool
,
int
&
)
{
inline
bool
grisu
_format
(
Double
,
buffer
<
char
>&
,
int
,
unsigned
,
int
&
)
{
return
false
;
}
...
...
@@ -1166,8 +1172,8 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
// The number is given as v = digits * pow(10, exp).
template
<
typename
Char
,
typename
It
>
It
grisu
2
_prettify
(
const
char
*
digits
,
int
size
,
int
exp
,
It
it
,
gen_digits_params
params
)
{
It
grisu_prettify
(
const
char
*
digits
,
int
size
,
int
exp
,
It
it
,
gen_digits_params
params
)
{
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int
full_exp
=
size
+
exp
;
if
(
!
params
.
fixed
)
{
...
...
@@ -1213,6 +1219,8 @@ It grisu2_prettify(const char* digits, int size, int exp, It it,
if
(
params
.
num_digits
>=
0
&&
params
.
num_digits
<
num_zeros
)
num_zeros
=
params
.
num_digits
;
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
if
(
!
params
.
trailing_zeros
)
while
(
size
>
0
&&
digits
[
size
-
1
]
==
'0'
)
--
size
;
it
=
copy_str
<
Char
>
(
digits
,
digits
+
size
,
it
);
}
return
it
;
...
...
@@ -2661,7 +2669,7 @@ template <typename Range> class basic_writer {
int
full_exp
=
num_digits
+
exp
-
1
;
int
precision
=
params
.
num_digits
>
0
?
params
.
num_digits
:
11
;
params_
.
fixed
|=
full_exp
>=
-
4
&&
full_exp
<
precision
;
auto
it
=
internal
::
grisu
2
_prettify
<
char
>
(
auto
it
=
internal
::
grisu_prettify
<
char
>
(
digits
.
data
(),
num_digits
,
exp
,
internal
::
counting_iterator
<
char
>
(),
params_
);
size_
=
it
.
count
();
...
...
@@ -2673,8 +2681,8 @@ template <typename Range> class basic_writer {
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign_
)
*
it
++
=
static_cast
<
char_type
>
(
sign_
);
int
num_digits
=
static_cast
<
int
>
(
digits_
.
size
());
it
=
internal
::
grisu
2_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
exp_
,
it
,
params_
);
it
=
internal
::
grisu
_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
exp_
,
it
,
params_
);
}
};
...
...
@@ -2874,11 +2882,12 @@ void basic_writer<Range>::write_double(T value, const format_specs& spec) {
memory_buffer
buffer
;
int
exp
=
0
;
int
precision
=
spec
.
has_precision
()
||
!
spec
.
type
?
spec
.
precision
:
6
;
unsigned
options
=
handler
.
fixed
?
internal
::
grisu_options
::
fixed
:
0
;
bool
use_grisu
=
fmt
::
internal
::
use_grisu
<
T
>
()
&&
(
spec
.
type
!=
'a'
&&
spec
.
type
!=
'A'
&&
spec
.
type
!=
'e'
&&
spec
.
type
!=
'E'
)
&&
internal
::
grisu
2
_format
(
static_cast
<
double
>
(
value
),
buffer
,
precision
,
handler
.
fixed
,
exp
);
internal
::
grisu_format
(
static_cast
<
double
>
(
value
),
buffer
,
precision
,
options
,
exp
);
if
(
!
use_grisu
)
internal
::
sprintf_format
(
value
,
buffer
,
spec
);
if
(
handler
.
as_percentage
)
{
...
...
src/format.cc
View file @
b3cc9c05
...
...
@@ -10,9 +10,9 @@
FMT_BEGIN_NAMESPACE
template
struct
internal
::
basic_data
<
void
>;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu
2
_format.
bool
(
*
instantiate_grisu
2_format
)(
double
,
internal
::
buffer
<
char
>&
,
int
,
bool
,
int
&
)
=
internal
::
grisu2
_format
;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool
(
*
instantiate_grisu
_format
)(
double
,
internal
::
buffer
<
char
>&
,
int
,
unsigned
,
int
&
)
=
internal
::
grisu
_format
;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template
FMT_API
internal
::
locale_ref
::
locale_ref
(
const
std
::
locale
&
loc
);
...
...
test/chrono-test.cc
View file @
b3cc9c05
...
...
@@ -305,4 +305,5 @@ TEST(ChronoTest, InvalidColons) {
EXPECT_THROW
(
fmt
::
format
(
"{0}=:{0::"
,
std
::
chrono
::
seconds
(
0
)),
fmt
::
format_error
);
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
test/format-impl-test.cc
View file @
b3cc9c05
...
...
@@ -137,10 +137,10 @@ TEST(FPTest, FixedHandler) {
digits
::
error
);
}
TEST
(
FPTest
,
Grisu
2
FormatCompilesWithNonIEEEDouble
)
{
TEST
(
FPTest
,
GrisuFormatCompilesWithNonIEEEDouble
)
{
fmt
::
memory_buffer
buf
;
int
exp
=
0
;
grisu
2
_format
(
4.2
f
,
buf
,
-
1
,
false
,
exp
);
grisu_format
(
4.2
f
,
buf
,
-
1
,
false
,
exp
);
}
template
<
typename
T
>
struct
ValueExtractor
:
fmt
::
internal
::
function
<
T
>
{
...
...
test/format-test.cc
View file @
b3cc9c05
...
...
@@ -1488,6 +1488,7 @@ TEST(FormatterTest, PrecisionRounding) {
EXPECT_EQ
(
"0.002"
,
format
(
"{:.3f}"
,
0.0015
));
EXPECT_EQ
(
"1.000"
,
format
(
"{:.3f}"
,
0.9999
));
EXPECT_EQ
(
"0.00123"
,
format
(
"{:.3}"
,
0.00123
));
EXPECT_EQ
(
"0.1"
,
format
(
"{:.16g}"
,
0.1
));
// Trigger rounding error in Grisu by a carefully chosen number.
auto
n
=
3788512123356.985352
;
char
buffer
[
64
];
...
...
@@ -2451,7 +2452,7 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR
(
"{:d}"
,
"invalid type specifier"
,
std
::
string
);
EXPECT_ERROR
(
"{:s}"
,
"invalid type specifier"
,
void
*
);
# else
fmt
::
print
(
"warning: constexpr is broken in this versio of MSVC
\n
"
);
fmt
::
print
(
"warning: constexpr is broken in this versio
n
of MSVC
\n
"
);
# endif
EXPECT_ERROR
(
"{foo"
,
"compile-time checks don't support named arguments"
,
int
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment