Skip to content

Commit 99d3cc4

Browse files
committed
Merge branch 'pr/53'
2 parents cd2fc0c + e2994ca commit 99d3cc4

File tree

4 files changed

+37
-12
lines changed

4 files changed

+37
-12
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,11 @@ means that a `bool` variable printed with "%s" will come out as `true` or
202202
Not all features of printf can be simulated simply using standard iostreams.
203203
Here's a list of known incompatibilities:
204204
205-
* The C99 `"%a"` and `"%A"` hexadecimal floating point conversions are not
206-
supported since the iostreams don't have the necessary flags. Using either
207-
of these flags will result in a call to `TINYFORMAT_ERROR`.
205+
* The `"%a"` and `"%A"` hexadecimal floating point conversions ignore precision
206+
as stream output of hexfloat (introduced in C++11) ignores precision, always
207+
outputting the minimum number of digits required for exact representation.
208+
MSVC incorrectly honors stream precision, so we force precision to 13 in this
209+
case to guarentee lossless roundtrip conversion.
208210
* The precision for integer conversions cannot be supported by the iostreams
209211
state independently of the field width. (Note: **this is only a
210212
problem for certain obscure integer conversions**; float conversions like

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ build_script:
4848

4949
test_script:
5050
# cmake testall target has problems finding the correct configuration, so use ctest directly.
51-
- ctest -C %CONFIGURATION%
51+
- ctest -VV -C %CONFIGURATION%
5252

tinyformat.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ namespace detail {
496496

497497
// Type-opaque holder for an argument to format(), with associated actions on
498498
// the type held as explicit function pointers. This allows FormatArg's for
499-
// each argument to be allocated as a homogenous array inside FormatList
499+
// each argument to be allocated as a homogeneous array inside FormatList
500500
// whereas a naive implementation based on inheritance does not.
501501
class FormatArg
502502
{
@@ -838,6 +838,18 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
838838
case 'f':
839839
out.setf(std::ios::fixed, std::ios::floatfield);
840840
break;
841+
case 'A':
842+
out.setf(std::ios::uppercase);
843+
// Falls through
844+
case 'a':
845+
# ifdef _MSC_VER
846+
// Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html
847+
// by always setting maximum precision on MSVC to avoid precision
848+
// loss for doubles.
849+
out.precision(13);
850+
# endif
851+
out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield);
852+
break;
841853
case 'G':
842854
out.setf(std::ios::uppercase);
843855
// Falls through
@@ -846,17 +858,13 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
846858
// As in boost::format, let stream decide float format.
847859
out.flags(out.flags() & ~std::ios::floatfield);
848860
break;
849-
case 'a': case 'A':
850-
TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs "
851-
"are not supported");
852-
break;
853861
case 'c':
854862
// Handled as special case inside formatValue()
855863
break;
856864
case 's':
857865
if(precisionSet)
858866
ntrunc = static_cast<int>(out.precision());
859-
// Make %s print booleans as "true" and "false"
867+
// Make %s print Booleans as "true" and "false"
860868
out.setf(std::ios::boolalpha);
861869
break;
862870
case 'n':

tinyformat_test.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ int unitTests()
122122
CHECK_EQUAL(tfm::format("%E", -1.23456E10), "-1.234560E+10");
123123
CHECK_EQUAL(tfm::format("%f", -9.8765), "-9.876500");
124124
CHECK_EQUAL(tfm::format("%F", 9.8765), "9.876500");
125+
# ifndef _MSC_VER
126+
CHECK_EQUAL(tfm::format("%a", -1.671111047267913818359375), "-0x1.abcdefp+0");
127+
CHECK_EQUAL(tfm::format("%A", 1.671111047267913818359375), "0X1.ABCDEFP+0");
128+
# else
129+
CHECK_EQUAL(tfm::format("%a", -1.671111047267913818359375), "-0x1.abcdef0000000p+0");
130+
CHECK_EQUAL(tfm::format("%A", 1.671111047267913818359375), "0X1.ABCDEF0000000P+0");
131+
# endif
125132
CHECK_EQUAL(tfm::format("%g", 10), "10");
126133
CHECK_EQUAL(tfm::format("%G", 100), "100");
127134
CHECK_EQUAL(tfm::format("%c", 65), "A");
@@ -163,6 +170,16 @@ int unitTests()
163170
CHECK_EQUAL(tfm::format("%.4d", 10), "0010");
164171
CHECK_EQUAL(tfm::format("%10.4f", 1234.1234567890), " 1234.1235");
165172
CHECK_EQUAL(tfm::format("%.f", 10.1), "10");
173+
// Per C++ spec, iostreams ignore the precision for "%a" to avoid precision
174+
// loss. This is a printf incompatibility.
175+
# ifndef _MSC_VER
176+
CHECK_EQUAL(tfm::format("%.1a", 1.13671875), "0x1.23p+0");
177+
CHECK_EQUAL(tfm::format("%14a", 1.671111047267913818359375), " 0x1.abcdefp+0");
178+
# else
179+
// MSVC workaround
180+
CHECK_EQUAL(tfm::format("%.1a", 1.13671875), "0x1.2300000000000p+0");
181+
CHECK_EQUAL(tfm::format("%21a", 1.671111047267913818359375), " 0x1.abcdef0000000p+0");
182+
# endif
166183
CHECK_EQUAL(tfm::format("%.2s", "asdf"), "as"); // strings truncate to precision
167184
CHECK_EQUAL(tfm::format("%.2s", std::string("asdf")), "as");
168185
// Test variable precision & width
@@ -245,8 +262,6 @@ int unitTests()
245262

246263
// Unhandled C99 format spec
247264
EXPECT_ERROR( tfm::format("%n", 10) )
248-
EXPECT_ERROR( tfm::format("%a", 10) )
249-
EXPECT_ERROR( tfm::format("%A", 10) )
250265

251266
#ifdef TEST_WCHAR_T_COMPILE
252267
// Test wchar_t handling - should fail to compile!

0 commit comments

Comments
 (0)