Skip to content

Commit e2994ca

Browse files
Chris Fosterc42f
authored andcommitted
Workaround MSVC precision handling with "%a" conversion
Force precision to 13 in this case to guarentee roundtripping of double precision hexfloat formatted numbers with MSVC.
1 parent 76366f1 commit e2994ca

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,9 @@ Here's a list of known incompatibilities:
204204
205205
* The `"%a"` and `"%A"` hexadecimal floating point conversions ignore precision
206206
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 - the default precision of 6 digits
209-
risks losing precision by truncation so to guarantee lossless roundtrip
210-
conversion "%.13a" will output full precision padded with 0's as needed).
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.
211210
* The precision for integer conversions cannot be supported by the iostreams
212211
state independently of the field width. (Note: **this is only a
213212
problem for certain obscure integer conversions**; float conversions like

tinyformat.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,12 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
842842
out.setf(std::ios::uppercase);
843843
// Falls through
844844
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
845851
out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield);
846852
break;
847853
case 'G':

tinyformat_test.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +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
125126
CHECK_EQUAL(tfm::format("%a", -1.671111047267913818359375), "-0x1.abcdefp+0");
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
127132
CHECK_EQUAL(tfm::format("%g", 10), "10");
128133
CHECK_EQUAL(tfm::format("%G", 100), "100");
129134
CHECK_EQUAL(tfm::format("%c", 65), "A");
@@ -165,9 +170,17 @@ int unitTests()
165170
CHECK_EQUAL(tfm::format("%.4d", 10), "0010");
166171
CHECK_EQUAL(tfm::format("%10.4f", 1234.1234567890), " 1234.1235");
167172
CHECK_EQUAL(tfm::format("%.f", 10.1), "10");
168-
CHECK_EQUAL(tfm::format("%.13a", 0.1), "0x1.999999999999ap-4");
169-
CHECK_EQUAL(tfm::format("%14a", 1.671111047267913818359375), " 0x1.abcdefp+0");
170-
CHECK_EQUAL(tfm::format("%.2s", "asdf"), "as"); // strings truncate to precision
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
183+
CHECK_EQUAL(tfm::format("%.2s", "asdf"), "as"); // strings truncate to precision
171184
CHECK_EQUAL(tfm::format("%.2s", std::string("asdf")), "as");
172185
// Test variable precision & width
173186
CHECK_EQUAL(tfm::format("%*.4f", 10, 1234.1234567890), " 1234.1235");

0 commit comments

Comments
 (0)