Are std::showbase and std::showpos mutually exclusive?

吃可爱长大的小学妹 提交于 2019-12-06 03:58:18

问题


This question arose from a discussion I was having about the correct way to output a numeric value using the usual ostream & operator << (ostream &, some_type) for a numeric type in C++.

The way I'm familiar with the behavior of std::showbase and std::showpos in each base, they are basically mutually exclusive. That is: in decimal no base is shown, and the '+' is added on positive numbers; whereas in hexadecimal or octal, the base is shown, but a '+' is not shown (nor is a minus), as the value of the type is printed as if it's cast to an unsigned type.

For example, this simple (verbose) program:

#include <iostream>

int main() {
  std::cout << std::dec << std::showpos << std::showbase << int64_t(+5) << std::endl;
  std::cout << std::oct << std::showpos << std::showbase << int64_t(+5) << std::endl;
  std::cout << std::hex << std::showpos << std::showbase << int64_t(+5) << std::endl;
  std::cout << std::dec << std::showpos << std::showbase << int64_t(-5) << std::endl;
  std::cout << std::oct << std::showpos << std::showbase << int64_t(-5) << std::endl;
  std::cout << std::hex << std::showpos << std::showbase << int64_t(-5) << std::endl;
}

Gives this output when compiled with GCC:

+5
05
0x5
-5
01777777777777777777773
0xfffffffffffffffb

This is what I've always expected, having used C++ for many years, but is this really guaranteed by the standard, or is this just common behavior? For example, could a standard-compliant C++ compiler output one of these sequences instead?

+5
+05
+0x5
-5
01777777777777777777773
0xfffffffffffffffb

or even:

+5
+05
+0x5
-5
-05
-0x5

回答1:


For ios_base itself, no. showpos and showbase calls the single-argument setf (§27.5.6.1[fmtflags.manip]/5 and /13) on the stream, which the two does not affect each other.


Going deeper, a std::ostream uses the locale::facet::put function to print an integer (§27.7.3.6.2[ostream.inserters.arithmetic]/1), and its implementation locale::facet::do_put (§22.4.2.2.2[facet.num.put.virtuals]/5) specifies:

All tables used in describing stage 1 are ordered. That is, the first line whose condition is true applies. A line without a condition is the default behavior when none of the earlier lines apply.

...

The conversion specifier has the following optional additional qualifiers prepended as indicated in Table 90.

                  Table 90 — Numeric conversions

+-----------------------+-------------------+------------------+
| Type(s)               | State             | stdio equivalent |
+=======================+===================+==================+
|                       | flags & showpos   | +                |
| an integral type      |                   |                  |
|                       | flags & showbase  | #                |
+-----------------------+-------------------+------------------+
|                       | flags & showpos   | +                |
| a floating-point type |                   |                  |
|                       | flags & showpoint | #                |
+-----------------------+-------------------+------------------+

...

The representations at the end of stage 1 consists of the char’s that would be printed by a call of printf(s, val) where s is the conversion specifier determined above.

Here, we see that showpos and showbase are in the same cell, which, I believe the standard implicitly mean that they're in the same "line", and thus both applies (it can be seen from std::cout << std::showpos << std::showpoint << 6.0 for the following "line"), and the two flags are still not mutually exclusive here.


So far, we see that showpos and showbase are not exclusive in C++, and the actual formatting behavior is defined by printf (though the implementation doesn't need to use printf, e.g. libc++ uses sprintf, while libstdc++ does not), which we have to check the C standard.

In C, using + (showpos) with o and x/X (oct and hex) is not defined, because C99 §7.19.6.1/6 and /8 says

+

The result of a signed conversion always begins with a plus or minus sign. ...

o, u, x, X

The unsigned int argument is converted to ...

The argument is not signed, so the + cannot apply. The behavior is not written out, so it is undefined.

Adding # (showbase) to d (dec) is also undefined behavior, as clause /6 says:

#

The result is converted to an “alternative form”. For o conversion, ... For x (or X) conversion, ... For a, A, e, E, f, F, g, and G conversions, ... For g and G conversions, ... For other conversions, the behavior is undefined.

Oops.

Therefore, not only the two flags are not mutually exclusive, the output is not defined at all. Scenarios 2 and 3 OP mentioned may happen. In gcc and clang, the conflicting option (showpos for oct and hex; showbase for dec) are simply ignored, which gives one an illusion of the two options being mutually exclusive, but the standard will not guarentee that.

(Disclaimer: I am using n3242 and n1124 as reference, the final standard might not be exactly the same)




回答2:


Bit of googling found me this page which says the following on the subject:

Note that negative integers are not printed as such in octal or hexadecimal. Rather, the internal bit patterns are interpreted as always being positive values.

If this is accurate, it would make sense that showpos would do nothing, why show a + in front of a number that is always positive?



来源:https://stackoverflow.com/questions/8424260/are-stdshowbase-and-stdshowpos-mutually-exclusive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!