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
8d8ea21c
Commit
8d8ea21c
authored
Apr 27, 2019
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Partially implement Grisu3
parent
40a79756
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
81 additions
and
37 deletions
+81
-37
include/fmt/format-inl.h
include/fmt/format-inl.h
+58
-21
include/fmt/format.h
include/fmt/format.h
+18
-11
src/format.cc
src/format.cc
+3
-3
test/format-impl-test.cc
test/format-impl-test.cc
+2
-2
No files found.
include/fmt/format-inl.h
View file @
8d8ea21c
...
@@ -494,10 +494,12 @@ enum result {
...
@@ -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
>
template
<
typename
Handler
>
digits
::
result
grisu
2
_gen_digits
(
fp
value
,
uint64_t
error
,
int
&
exp
,
digits
::
result
grisu_gen_digits
(
fp
value
,
uint64_t
error
,
int
&
exp
,
Handler
&
handler
)
{
Handler
&
handler
)
{
fp
one
(
1ull
<<
-
value
.
e
,
value
.
e
);
fp
one
(
1ull
<<
-
value
.
e
,
value
.
e
);
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
// 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
// zero because it contains a product of two 64-bit numbers with MSB set (due
...
@@ -635,34 +637,57 @@ struct fixed_handler {
...
@@ -635,34 +637,57 @@ struct fixed_handler {
};
};
// The shortest representation digit handler.
// The shortest representation digit handler.
struct
shortest_handler
{
template
<
int
GRISU_VERSION
>
struct
grisu_
shortest_handler
{
char
*
buf
;
char
*
buf
;
int
size
;
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
&
)
{
digits
::
result
on_start
(
uint64_t
,
uint64_t
,
uint64_t
,
int
&
)
{
return
digits
::
more
;
return
digits
::
more
;
}
}
// This implements Grisu3's round_weed.
digits
::
result
on_digit
(
char
digit
,
uint64_t
divisor
,
uint64_t
remainder
,
digits
::
result
on_digit
(
char
digit
,
uint64_t
divisor
,
uint64_t
remainder
,
uint64_t
error
,
int
exp
,
bool
integral
)
{
uint64_t
error
,
int
exp
,
bool
integral
)
{
buf
[
size
++
]
=
digit
;
buf
[
size
++
]
=
digit
;
if
(
remainder
>
error
)
return
digits
::
more
;
if
(
remainder
>=
error
)
return
digits
::
more
;
uint64_t
d
=
integral
?
diff
.
f
:
diff
.
f
*
data
::
POWERS_OF_10_64
[
-
exp
];
if
(
GRISU_VERSION
!=
3
)
{
while
(
uint64_t
d
=
integral
?
diff
:
diff
*
data
::
POWERS_OF_10_64
[
-
exp
];
remainder
<
d
&&
error
-
remainder
>=
divisor
&&
while
(
remainder
<
d
&&
error
-
remainder
>=
divisor
&&
(
remainder
+
divisor
<
d
||
d
-
remainder
>
remainder
+
divisor
-
d
))
{
(
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
];
--
buf
[
size
-
1
];
remainder
+=
divisor
;
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
<
template
<
typename
Double
,
typename
std
::
enable_if
<
sizeof
(
Double
)
==
sizeof
(
uint64_t
),
int
>
::
type
>
sizeof
(
Double
)
==
sizeof
(
uint64_t
),
int
>
::
type
>
FMT_FUNC
bool
grisu
2
_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
FMT_FUNC
bool
grisu_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
bool
fixed
,
int
&
exp
)
{
unsigned
options
,
int
&
exp
)
{
FMT_ASSERT
(
value
>=
0
,
"value is negative"
);
FMT_ASSERT
(
value
>=
0
,
"value is negative"
);
bool
fixed
=
(
options
&
grisu_options
::
fixed
)
!=
0
;
if
(
value
<=
0
)
{
// <= instead of == to silence a warning.
if
(
value
<=
0
)
{
// <= instead of == to silence a warning.
if
(
precision
<
0
||
!
fixed
)
{
if
(
precision
<
0
||
!
fixed
)
{
exp
=
0
;
exp
=
0
;
...
@@ -685,7 +710,7 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
...
@@ -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
);
min_exp
-
(
fp_value
.
e
+
fp
::
significand_size
),
cached_exp10
);
fp_value
=
fp_value
*
cached_pow
;
fp_value
=
fp_value
*
cached_pow
;
fixed_handler
handler
{
buf
.
data
(),
0
,
precision
,
-
cached_exp10
,
fixed
};
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
;
return
false
;
buf
.
resize
(
to_unsigned
(
handler
.
size
));
buf
.
resize
(
to_unsigned
(
handler
.
size
));
}
else
{
}
else
{
...
@@ -695,17 +720,29 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
...
@@ -695,17 +720,29 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
// the exponent in the range [min_exp, -32].
// the exponent in the range [min_exp, -32].
auto
cached_pow
=
get_cached_power
(
// \tilde{c}_{-k} in Grisu.
auto
cached_pow
=
get_cached_power
(
// \tilde{c}_{-k} in Grisu.
min_exp
-
(
upper
.
e
+
fp
::
significand_size
),
cached_exp10
);
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
.
normalize
();
fp_value
=
fp_value
*
cached_pow
;
fp_value
=
fp_value
*
cached_pow
;
lower
=
lower
*
cached_pow
;
// \tilde{M}^- in Grisu.
lower
=
lower
*
cached_pow
;
// \tilde{M}^- in Grisu.
++
lower
.
f
;
// \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
upper
=
upper
*
cached_pow
;
// \tilde{M}^+ in Grisu.
shortest_handler
handler
{
buf
.
data
(),
0
,
upper
-
fp_value
};
assert
(
min_exp
<=
upper
.
e
&&
upper
.
e
<=
-
32
);
auto
result
=
grisu2_gen_digits
(
upper
,
upper
.
f
-
lower
.
f
,
exp
,
handler
);
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
;
if
(
result
==
digits
::
error
)
return
false
;
buf
.
resize
(
to_unsigned
(
handler
.
size
));
buf
.
resize
(
to_unsigned
(
size
));
}
}
exp
-=
cached_exp10
;
exp
-=
cached_exp10
;
return
true
;
return
true
;
...
...
include/fmt/format.h
View file @
8d8ea21c
...
@@ -1124,13 +1124,19 @@ FMT_CONSTEXPR unsigned basic_parse_context<Char, ErrorHandler>::next_arg_id() {
...
@@ -1124,13 +1124,19 @@ FMT_CONSTEXPR unsigned basic_parse_context<Char, ErrorHandler>::next_arg_id() {
namespace
internal
{
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
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
==
sizeof
(
uint64_t
))>
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
==
sizeof
(
uint64_t
))>
FMT_API
bool
grisu2_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
FMT_API
bool
grisu_format
(
Double
,
buffer
<
char
>&
,
int
,
unsigned
,
int
&
);
bool
fixed
,
int
&
exp
);
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
!=
sizeof
(
uint64_t
))>
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
;
return
false
;
}
}
...
@@ -1166,8 +1172,8 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
...
@@ -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).
// The number is given as v = digits * pow(10, exp).
template
<
typename
Char
,
typename
It
>
template
<
typename
Char
,
typename
It
>
It
grisu
2
_prettify
(
const
char
*
digits
,
int
size
,
int
exp
,
It
it
,
It
grisu_prettify
(
const
char
*
digits
,
int
size
,
int
exp
,
It
it
,
gen_digits_params
params
)
{
gen_digits_params
params
)
{
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int
full_exp
=
size
+
exp
;
int
full_exp
=
size
+
exp
;
if
(
!
params
.
fixed
)
{
if
(
!
params
.
fixed
)
{
...
@@ -2663,7 +2669,7 @@ template <typename Range> class basic_writer {
...
@@ -2663,7 +2669,7 @@ template <typename Range> class basic_writer {
int
full_exp
=
num_digits
+
exp
-
1
;
int
full_exp
=
num_digits
+
exp
-
1
;
int
precision
=
params
.
num_digits
>
0
?
params
.
num_digits
:
11
;
int
precision
=
params
.
num_digits
>
0
?
params
.
num_digits
:
11
;
params_
.
fixed
|=
full_exp
>=
-
4
&&
full_exp
<
precision
;
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
>
(),
digits
.
data
(),
num_digits
,
exp
,
internal
::
counting_iterator
<
char
>
(),
params_
);
params_
);
size_
=
it
.
count
();
size_
=
it
.
count
();
...
@@ -2675,8 +2681,8 @@ template <typename Range> class basic_writer {
...
@@ -2675,8 +2681,8 @@ template <typename Range> class basic_writer {
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign_
)
*
it
++
=
static_cast
<
char_type
>
(
sign_
);
if
(
sign_
)
*
it
++
=
static_cast
<
char_type
>
(
sign_
);
int
num_digits
=
static_cast
<
int
>
(
digits_
.
size
());
int
num_digits
=
static_cast
<
int
>
(
digits_
.
size
());
it
=
internal
::
grisu
2_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
it
=
internal
::
grisu
_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
exp_
,
exp_
,
it
,
params_
);
it
,
params_
);
}
}
};
};
...
@@ -2876,11 +2882,12 @@ void basic_writer<Range>::write_double(T value, const format_specs& spec) {
...
@@ -2876,11 +2882,12 @@ void basic_writer<Range>::write_double(T value, const format_specs& spec) {
memory_buffer
buffer
;
memory_buffer
buffer
;
int
exp
=
0
;
int
exp
=
0
;
int
precision
=
spec
.
has_precision
()
||
!
spec
.
type
?
spec
.
precision
:
6
;
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
>
()
&&
bool
use_grisu
=
fmt
::
internal
::
use_grisu
<
T
>
()
&&
(
spec
.
type
!=
'a'
&&
spec
.
type
!=
'A'
&&
spec
.
type
!=
'e'
&&
(
spec
.
type
!=
'a'
&&
spec
.
type
!=
'A'
&&
spec
.
type
!=
'e'
&&
spec
.
type
!=
'E'
)
&&
spec
.
type
!=
'E'
)
&&
internal
::
grisu
2
_format
(
static_cast
<
double
>
(
value
),
buffer
,
internal
::
grisu_format
(
static_cast
<
double
>
(
value
),
buffer
,
precision
,
handler
.
fixed
,
exp
);
precision
,
options
,
exp
);
if
(
!
use_grisu
)
internal
::
sprintf_format
(
value
,
buffer
,
spec
);
if
(
!
use_grisu
)
internal
::
sprintf_format
(
value
,
buffer
,
spec
);
if
(
handler
.
as_percentage
)
{
if
(
handler
.
as_percentage
)
{
...
...
src/format.cc
View file @
8d8ea21c
...
@@ -10,9 +10,9 @@
...
@@ -10,9 +10,9 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_NAMESPACE
template
struct
internal
::
basic_data
<
void
>;
template
struct
internal
::
basic_data
<
void
>;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu
2
_format.
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool
(
*
instantiate_grisu
2_format
)(
double
,
internal
::
buffer
<
char
>&
,
int
,
bool
,
bool
(
*
instantiate_grisu
_format
)(
double
,
internal
::
buffer
<
char
>&
,
int
,
unsigned
,
int
&
)
=
internal
::
grisu2
_format
;
int
&
)
=
internal
::
grisu
_format
;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template
FMT_API
internal
::
locale_ref
::
locale_ref
(
const
std
::
locale
&
loc
);
template
FMT_API
internal
::
locale_ref
::
locale_ref
(
const
std
::
locale
&
loc
);
...
...
test/format-impl-test.cc
View file @
8d8ea21c
...
@@ -137,10 +137,10 @@ TEST(FPTest, FixedHandler) {
...
@@ -137,10 +137,10 @@ TEST(FPTest, FixedHandler) {
digits
::
error
);
digits
::
error
);
}
}
TEST
(
FPTest
,
Grisu
2
FormatCompilesWithNonIEEEDouble
)
{
TEST
(
FPTest
,
GrisuFormatCompilesWithNonIEEEDouble
)
{
fmt
::
memory_buffer
buf
;
fmt
::
memory_buffer
buf
;
int
exp
=
0
;
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
>
{
template
<
typename
T
>
struct
ValueExtractor
:
fmt
::
internal
::
function
<
T
>
{
...
...
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