Boost Karma 事始め
Boost Spirit のパーサ Qi に対応するジェネレータ Karma を使うと、以下のように型安全な printf
風の出力が簡単に行える。
#include <iostream>
#include <cmath>
#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
long double v[] = { std::exp(1), std::sqrt(2), };
std::cout << karma::format(karma::long_double << ',' << karma::long_double, v[0], v[1]) << std::endl;
return 0;
}
2.718,1.414
それどころか、様々なコンテナに対応しているので、std::vector
に格納された要素の出力も極めて簡単である。
#include <iostream>
#include <cmath>
#include <vector>
#include <boost/assign.hpp>
#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
std::vector<long double> v = boost::assign::list_of(std::exp(1))(std::sqrt(2));
std::cout << karma::format(karma::long_double % ',', v) << std::endl;
return 0;
}
2.718,1.414
Boost Karma による C99 printf double ステージ1
しかし、fprintf
のような書式指定を行おうとすると少々厄介なので、ここでは浮動小数点に関して C99 printf 互換のジェネレータを用意してしまおう。
具体的には、以下のような C99 printf 書式指定のような浮動小数点の表示を、段階的に可能としていこう。
scientific_real
%.3e 1.000e+00
%e 1.000000e+00
%+e +1.000000e+00
% e 1.000000e+00
fixed_real
%.3f 1.000
%f 1.000000
%+f +1.000000
% f 1.000000
general_real
%.3g 1
%g 1
%+g +1
% g 1
alt_general_real
%#.3g 1.00
%#g 1.00000
%#+g +1.00000
%# g 1.00000
ちなみに、幅指定は最後の最後で対応する。
さて、Karma における浮動小数点の書式指定は real_policies<T>
を継承したクラスにて行うものとなっているので、まずはこれらの「ポリシー」を用意しよう。
/*
boost-spirit-karma-real_generators.hpp
Copyright (C) 2013 Taiji Yamada <taiji@aihara.co.jp>
Distributed under the Boost Software License, Version 1.0.
(See http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef _boost_spirit_karma_real_generators_hpp_
#define _boost_spirit_karma_real_generators_hpp_
#include <boost/spirit/include/karma.hpp>
namespace boost { namespace spirit { namespace karma {
template <typename T>
struct scientific_real_policies : real_policies<T> {
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
};
template <typename T>
struct fixed_real_policies : scientific_real_policies<T> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
};
template <typename T>
struct alt_general_real_policies : fixed_real_policies<T> {
static int floatfield(T n)
{
return real_policies<T>::floatfield(n);
}
};
template <typename T>
struct general_real_policies : alt_general_real_policies<T> {
};
} } }
#endif
floatfield(T n)
が常に real_policies<T>::fmtflags::scientific
を返せば %e
相当、常に real_policies<T>::fmtflags::fixed
を返せば %f
相当、既定の real_policies<T>::floatfield(n)
に従えば %g
相当のようである。
例えばこれは、以下のように利用する。
#include <iostream>
#include <cmath>
#include <boost/spirit/include/karma.hpp>
#include "boost-spirit-karma-real_generators00.hpp"
namespace karma = boost::spirit::karma;
typedef karma::real_generator<long double, karma::scientific_real_policies<long double> > long_double_generator;
int main()
{
long_double_generator long_double;
long double v[] = { std::exp(1), std::sqrt(2), };
std::cout << karma::format(long_double << ',' << long_double, v[0], v[1]) << std::endl;
return 0;
}
この既定の段階では、以下のような表示形式になっている。
scientific_real_policies<T>
%e 1.0e00 T=double
fixed_real_policies<T>
%f 1.0 T=double
general_real_policies<T>
%g 1.0 T=double
まずは、精度を返す precision(T n)
および、符号を正符号においても強制的に印字するか否かを返す force_sign(T n)
で利用者がテンプレート引数で指定した属性を返すようにする。
--- boost-spirit-karma-real_generators00.hpp 2013-06-04 16:57:44.000000000 +0900
+++ boost-spirit-karma-real_generators01.hpp 2013-06-04 16:57:44.000000000 +0900
@@ -13,26 +13,28 @@
namespace boost { namespace spirit { namespace karma {
-template <typename T>
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
struct scientific_real_policies : real_policies<T> {
+ static bool force_sign(T) { return ForceSign; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
+ static unsigned precision(T) { return Precision; }
};
-template <typename T>
-struct fixed_real_policies : scientific_real_policies<T> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
};
-template <typename T>
-struct alt_general_real_policies : fixed_real_policies<T> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign> {
static int floatfield(T n)
{
return real_policies<T>::floatfield(n);
}
};
-template <typename T>
-struct general_real_policies : alt_general_real_policies<T> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign> {
};
}}}
Karma の既定の精度が 3 に対して、C99 printf の既定の精度は 6 なので、そのようにした。
この段階で以下のような表示形式になっている。
scientific_real_policies<T, Precision=6, ForceSign=false>
%.3e 1.0e00 T=double, Precision=3, ForceSign=false
%e 1.0e00 T=double, Precision=6, ForceSign=false
%+e +1.0e00 T=double, Precision=6, ForceSign=true
fixed_real_policies<T, Precision=6, ForceSign=false>
%.3f 1.0 T=double, Precision=3, ForceSign=false
%f 1.0 T=double, Precision=6, ForceSign=false
%+f +1.0 T=double, Precision=6, ForceSign=true
general_real_policies<T, Precision=6, ForceSign=false>
%.3g 1.0 T=double, Precision=3, ForceSign=false
%g 1.0 T=double, Precision=6, ForceSign=false
%+g +1.0 T=double, Precision=6, ForceSign=true
キリの良い数字の場合、ゼロが切り詰められているのは、%g
以外では意図した書式ではないので、それに trailing_zeros(T n)
で対応しよう。
--- boost-spirit-karma-real_generators01.hpp 2013-06-04 16:57:44.000000000 +0900
+++ boost-spirit-karma-real_generators02.hpp 2013-06-04 16:57:44.000000000 +0900
@@ -16,6 +16,7 @@
template <typename T, unsigned Precision = 6, bool ForceSign = false>
struct scientific_real_policies : real_policies<T> {
static bool force_sign(T) { return ForceSign; }
+ static bool trailing_zeros(T) { return true; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
static unsigned precision(T) { return Precision; }
};
@@ -27,6 +28,7 @@
template <typename T, unsigned Precision = 6, bool ForceSign = false>
struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign> {
+ static bool trailing_zeros(T) { return true; }
static int floatfield(T n)
{
return real_policies<T>::floatfield(n);
@@ -35,6 +37,7 @@
template <typename T, unsigned Precision = 6, bool ForceSign = false>
struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign> {
+ static bool trailing_zeros(T) { return false; }
};
}}}
この段階で以下のような表示形式になる。
scientific_real_policies<T, Precision=6, ForceSign=false>
%.3e 1.000e00 T=double, Precision=3, ForceSign=false
%e 1.000000e00 T=double, Precision=6, ForceSign=false
%+e +1.000000e00 T=double, Precision=6, ForceSign=true
fixed_real_policies<T, Precision=6, ForceSign=false>
%.3f 1.000 T=double, Precision=3, ForceSign=false
%f 1.000000 T=double, Precision=6, ForceSign=false
%+f +1.000000 T=double, Precision=6, ForceSign=true
general_real_policies<T, Precision=6, ForceSign=false>
%.3g 1.0 T=double, Precision=3, ForceSign=false
%g 1.0 T=double, Precision=6, ForceSign=false
%+g +1.0 T=double, Precision=6, ForceSign=true
alt_general_real_policies<T, Precision=6, ForceSign=false>
%#.3g 1.000 T=double, Precision=3, ForceSign=false
%#g 1.000000 T=double, Precision=6, ForceSign=false
%#+g +1.000000 T=double, Precision=6, ForceSign=true
次に、%e
の指数部の符号は常に表示するようにしよう。
--- boost-spirit-karma-real_generators02.hpp 2013-06-04 22:38:15.000000000 +0900
+++ boost-spirit-karma-real_generators03.hpp 2013-06-04 22:38:15.000000000 +0900
@@ -17,10 +17,21 @@
struct scientific_real_policies : real_policies<T> {
static bool force_sign(T) { return ForceSign; }
static bool trailing_zeros(T) { return true; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
static unsigned precision(T) { return Precision; }
+ template <typename CharEncoding, typename Tag, typename OutputIterator>
+ static bool exponent(OutputIterator &sink, long n)
+ {
+ long abs_n = traits::get_absolute_value(n);
+ bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
+ sign_inserter::call(sink, /*traits::test_zero(n)*/false,
+ traits::test_negative(n), /*false*/true);
+ if (r && abs_n < 10) // the C99 Standard requires at least two digits in the exponent
+ r = char_inserter<CharEncoding, Tag>::call(sink, '0');
+ return r && int_inserter<10>::call(sink, abs_n);
+ }
};
template <typename T, unsigned Precision = 6, bool ForceSign = false>
struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
すると以下のような表示形式になる。
scientific_real_policies<T, Precision=6, ForceSign=false>
%.3e 1.000e+00 T=double, Precision=3, ForceSign=false
%e 1.000000e+00 T=double, Precision=6, ForceSign=false
%+e +1.000000e+00 T=double, Precision=6, ForceSign=true
sign_inserter::call
の第4引数が強制的に符号を印字するか否かを決定している。ちなみに、第2引数は、真のときに正符号の代わりに「空白」を印字してしまうので、ここでは偽を指定してそれを抑止している。
次に、正符号の代わりに「空白」を印字することを、利用者がテンプレート引数で選べるようにする。
--- boost-spirit-karma-real_generators03.hpp 2013-06-04 16:57:45.000000000 +0900
+++ boost-spirit-karma-real_generators04.hpp 2013-06-04 16:57:45.000000000 +0900
@@ -13,12 +13,18 @@
namespace boost { namespace spirit { namespace karma {
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
struct scientific_real_policies : real_policies<T> {
static bool force_sign(T) { return ForceSign; }
static bool trailing_zeros(T) { return true; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
static unsigned precision(T) { return Precision; }
+ template <typename OutputIterator>
+ static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
+ {
+ return sign_inserter::call(sink, (force_sign && !sign && BlankSign) || traits::test_zero(n), sign, force_sign) &&
+ int_inserter<10>::call(sink, n);
+ }
template <typename CharEncoding, typename Tag, typename OutputIterator>
static bool exponent(OutputIterator &sink, long n)
{
@@ -32,13 +38,18 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
-struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
+ template <typename OutputIterator>
+ static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
+ {
+ return real_policies<T>::integer_part(sink, n, sign, force_sign);
+ }
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
-struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
static bool trailing_zeros(T) { return true; }
static int floatfield(T n)
{
@@ -46,8 +57,8 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
-struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
static bool trailing_zeros(T) { return false; }
};
ここでは、scientific_real_policies<T>
の仮数部を印字する integer_part
内の sign_inserter::call
の第2引数は、真のときに正符号の代わりに「空白」を印字する条件を「正のとき空白としての符号が強制されるとき、または、仮数部がゼロのとき」として C99 printf 互換となるようにしている。
また、fixed_real_policies<T>
の仮数部を印字する integer_part
は、取り敢えず real_policies<T>
のものを踏襲している。
この段階で以下のような表示形式になる。
scientific_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3e 1.000e+00 T=double, Precision=3, ForceSign=false, BlankSign=false
%e 1.000000e+00 T=double, Precision=6, ForceSign=false, BlankSign=false
%+e +1.000000e+00 T=double, Precision=6, ForceSign=true, BlankSign=false
% e 1.000000e+00 T=double, Precision=6, ForceSign=true, BlankSign=true
fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f 1.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%f 1.000000 T=double, Precision=6, ForceSign=false, BlankSign=false
%+f +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=false
% f +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=true
general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3g 1.0 T=double, Precision=3, ForceSign=false, BlankSign=false
%g 1.0 T=double, Precision=6, ForceSign=false, BlankSign=false
%+g +1.0 T=double, Precision=6, ForceSign=true, BlankSign=false
% g +1.0 T=double, Precision=6, ForceSign=true, BlankSign=true
alt_general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%#.3g 1.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%#g 1.000000 T=double, Precision=6, ForceSign=false, BlankSign=false
%#+g +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=false
%# g +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=true
ここまでで %e
については意図した書式になったが、まだまだ C99 printf 互換には程遠い。
次に % f
に対応しよう。
--- boost-spirit-karma-real_generators04.hpp 2013-06-04 22:38:15.000000000 +0900
+++ boost-spirit-karma-real_generators05.hpp 2013-06-04 22:38:15.000000000 +0900
@@ -42,11 +42,12 @@
struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
template <typename OutputIterator>
static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
{
- return real_policies<T>::integer_part(sink, n, sign, force_sign);
+ return sign_inserter::call(sink, force_sign && !sign && BlankSign, sign, force_sign) &&
+ int_inserter<10>::call(sink, n);
}
};
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
ここでは、fixed_real_policies<T>
の仮数部を印字する integer_part
内の sign_inserter::call
の第2引数は、真のときに正符号の代わりに「空白」を印字する条件を「正のとき空白としての符号が強制されるとき」として C99 printf 互換となるようにしている。
次に、%g
では、キリの良い数字の場合、ゼロだけでなく小数点も不要だ。
--- boost-spirit-karma-real_generators05.hpp 2013-06-04 16:57:45.000000000 +0900
+++ boost-spirit-karma-real_generators06.hpp 2013-06-04 16:57:45.000000000 +0900
@@ -61,6 +61,26 @@
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
static bool trailing_zeros(T) { return false; }
+ static int floatfield(T n)
+ {
+ if (traits::test_zero(n))
+ return real_policies<T>::fmtflags::fixed;
+ return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::floatfield(n);
+ }
+ template <typename OutputIterator>
+ static bool dot(OutputIterator &sink, T n, unsigned precision)
+ {
+ if (traits::test_zero(n))
+ return true;
+ return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::dot(sink, n, precision);
+ }
+ template <typename OutputIterator>
+ static bool fraction_part(OutputIterator &sink, T n, unsigned adjprec, unsigned precision)
+ {
+ if (traits::test_zero(n))
+ return true;
+ return fixed_real_policies<T, Precision, ForceSign, BlankSign>::fraction_part(sink, n, adjprec, precision);
+ }
};
}}}
小数がゼロの時は real_policies<T>::fmtflags::fixed
で印字し、小数点や小数部は何もしない処理をしている。さもなくば、都合の良い継承しているメンバ関数を呼んでいる。
この段階で以下のような表示形式になる。
scientific_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3e 1.000e+00 T=double, Precision=3, ForceSign=false, BlankSign=false
%e 1.000000e+00 T=double, Precision=6, ForceSign=false, BlankSign=false
%+e +1.000000e+00 T=double, Precision=6, ForceSign=true, BlankSign=false
% e 1.000000e+00 T=double, Precision=6, ForceSign=true, BlankSign=true
fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f 1.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%f 1.000000 T=double, Precision=6, ForceSign=false, BlankSign=false
%+f +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=false
% f 1.000000 T=double, Precision=6, ForceSign=true, BlankSign=true
general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3g 1 T=double, Precision=3, ForceSign=false, BlankSign=false
%g 1 T=double, Precision=6, ForceSign=false, BlankSign=false
%+g +1 T=double, Precision=6, ForceSign=true, BlankSign=false
% g 1 T=double, Precision=6, ForceSign=true, BlankSign=true
alt_general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%#.3g 1.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%#g 1.000000 T=double, Precision=6, ForceSign=false, BlankSign=false
%#+g +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=false
%# g +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=true
相当よくなってきたが、致命的な問題が残っている。%#g
の精度とは「有効数字」のことであり、他における「小数点以下の桁数」ではない。よって、続けて floatfield(T n)
と precision(T n)
の2箇所修正してみよう。
--- boost-spirit-karma-real_generators06.hpp 2013-06-04 22:38:15.000000000 +0900
+++ boost-spirit-karma-real_generators07.hpp 2013-06-04 22:38:15.000000000 +0900
@@ -52,11 +52,14 @@
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
static bool trailing_zeros(T) { return true; }
static int floatfield(T n)
{
- return real_policies<T>::floatfield(n);
+ using namespace std;
+ T abs_n = traits::get_absolute_value(n);
+ return (!(floor(log10(abs_n)) < Precision) || abs_n < 1e-4) ?
+ real_policies<T>::fmtflags::scientific : real_policies<T>::fmtflags::fixed;
}
};
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
--- boost-spirit-karma-real_generators07.hpp 2013-06-04 16:57:45.000000000 +0900
+++ boost-spirit-karma-real_generators08.hpp 2013-06-04 16:57:45.000000000 +0900
@@ -59,6 +59,15 @@
return (!(floor(log10(abs_n)) < Precision) || abs_n < 1e-4) ?
real_policies<T>::fmtflags::scientific : real_policies<T>::fmtflags::fixed;
}
+ static unsigned precision(T n)
+ {
+ using namespace std;
+ T abs_n = traits::get_absolute_value(n), l;
+ if ((l = floor(log10(1./abs_n))) > 1)
+ return Precision + (l - 1);
+ l = floor(log10(abs_n));
+ return Precision > l ? Precision - (l + 1) : Precision - 1;
+ }
};
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
つまり、precision(T n)
は有効桁 Precision
から得られる小数点以下の桁数を返すようにし、他所との整合性をとった。
この段階で以下のような表示形式になる。
scientific_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3e 1.000e+00 T=double, Precision=3, ForceSign=false, BlankSign=false
%e 1.000000e+00 T=double, Precision=6, ForceSign=false, BlankSign=false
%+e +1.000000e+00 T=double, Precision=6, ForceSign=true, BlankSign=false
% e 1.000000e+00 T=double, Precision=6, ForceSign=true, BlankSign=true
fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f 1.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%f 1.000000 T=double, Precision=6, ForceSign=false, BlankSign=false
%+f +1.000000 T=double, Precision=6, ForceSign=true, BlankSign=false
% f 1.000000 T=double, Precision=6, ForceSign=true, BlankSign=true
general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3g 1 T=double, Precision=3, ForceSign=false, BlankSign=false
%g 1 T=double, Precision=6, ForceSign=false, BlankSign=false
%+g +1 T=double, Precision=6, ForceSign=true, BlankSign=false
% g 1 T=double, Precision=6, ForceSign=true, BlankSign=true
alt_general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%#.3g 1.00 T=double, Precision=3, ForceSign=false, BlankSign=false
%#g 1.00000 T=double, Precision=6, ForceSign=false, BlankSign=false
%#+g +1.00000 T=double, Precision=6, ForceSign=true, BlankSign=false
%# g 1.00000 T=double, Precision=6, ForceSign=true, BlankSign=true
ほぼ完璧と思ったら大間違い。精度が3で値が -0.0001
のように精度を超えた負値の場合、以下のように符号が消えてしまう。
fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f 0.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%#.3f 0.000 T=double, Precision=3, ForceSign=false, BlankSign=false
いくら精度以下とはいえ、負のほぼ零と正のほぼ零は意味が全く異る。
fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f -0.000 T=double, Precision=3, ForceSign=false, BlankSign=false
%#.3f -0.000 T=double, Precision=3, ForceSign=false, BlankSign=false
これに対応するには、結局、オリジナルが処理していることと微妙に異なるコードを書く必要があった。
--- boost-spirit-karma-real_generators08.hpp 2013-06-04 16:57:46.000000000 +0900
+++ boost-spirit-karma-real_generators09.hpp 2013-06-04 16:57:46.000000000 +0900
@@ -13,8 +13,74 @@
namespace boost { namespace spirit { namespace karma {
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
struct scientific_real_policies : real_policies<T> {
+ typedef CE CharEncoding;
+ typedef CC Tag;
+ template <typename Inserter, typename OutputIterator, typename Policies>
+ static bool call(OutputIterator &sink, T n, Policies const &p)
+ {
+ bool force_sign = p.force_sign(n);
+ bool sign_val = false;
+ int flags = p.floatfield(n);
+ if (traits::test_negative(n)) {
+ n = -n;
+ sign_val = true;
+ }
+ unsigned precision = p.precision(n);
+ if (std::numeric_limits<T>::digits10) {
+ precision = (std::min)(precision,
+ (unsigned)std::numeric_limits<T>::digits10 + 1);
+ }
+ using namespace std;
+ T dim = 0;
+ if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n)) {
+ dim = log10(n);
+ if (dim > 0)
+ n /= spirit::traits::pow10<T>(traits::truncate_to_long::call(dim));
+ else if (n < 1.) {
+ long exp = traits::truncate_to_long::call(-dim);
+ if (exp != -dim)
+ ++exp;
+ dim = -exp;
+ n *= spirit::traits::pow10<T>(exp);
+ }
+ }
+ T integer_part;
+ T precexp = spirit::traits::pow10<T>(precision);
+ T fractional_part = modf(n, &integer_part);
+ fractional_part = floor(fractional_part * precexp + T(0.5));
+ if (fractional_part >= precexp) {
+ fractional_part = floor(fractional_part - precexp);
+ integer_part += 1;
+ }
+ T long_int_part = floor(integer_part);
+ T long_frac_part = fractional_part;
+ unsigned prec = precision;
+ if (!p.trailing_zeros(n)) {
+ T frac_part_floor = long_frac_part;
+ if (0 != long_frac_part) {
+ while (0 != prec &&
+ 0 == traits::remainder<10>::call(long_frac_part)) {
+ long_frac_part = traits::divide<10>::call(long_frac_part);
+ --prec;
+ }
+ }
+ else
+ prec = 0;
+ if (precision != prec)
+ long_frac_part = frac_part_floor / spirit::traits::pow10<T>(precision-prec);
+ }
+ if (false &&
+ sign_val && traits::test_zero(long_int_part) && traits::test_zero(long_frac_part))
+ sign_val = false;
+ bool r = p.integer_part(sink, long_int_part, sign_val, force_sign);
+ r = r && p.dot(sink, long_frac_part, precision);
+ r = r && p.fraction_part(sink, long_frac_part, prec, precision);
+ if (r && 0 == (Policies::fmtflags::fixed & flags))
+ return p.template exponent<CharEncoding, Tag>(sink, traits::truncate_to_long::call(dim));
+ return r;
+ }
static bool force_sign(T) { return ForceSign; }
static bool trailing_zeros(T) { return true; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
@@ -38,8 +104,8 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
-struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
template <typename OutputIterator>
static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
@@ -49,8 +115,8 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
-struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
static bool trailing_zeros(T) { return true; }
static int floatfield(T n)
{
@@ -70,8 +136,8 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
-struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
static bool trailing_zeros(T) { return false; }
static int floatfield(T n)
{
call
でやっていることは sign_val = false
を抑制しているだけで、オリジナルとほぼ一緒。ついでに、大文字の %E
, %G
に対応させるための変更 typename CE = karma::char_encoding::ascii
, typename CC = karma::tag::upper
もやっているので注意。
Boost Karma による C99 printf double ステージ2
先の書式指定に加えて、以下のような幅指定及び各種フラグ -0
の追加に対応させよう。ひとつの値だけでも組み合わせはこれだけある。
%-12.3e 1.000e+00
%-12e 1.000000e+00
%-+12e +1.000000e+00
%- 12e 1.000000e+00
%-12.3f 1.000
%-12f 1.000000
%-+12f +1.000000
%- 12f 1.000000
%-12.3g 1
%-12g 1
%-+12g +1
%- 12g 1
%#-12.3g 1.00
%#-12g 1.00000
%#-+12g +1.00000
%#- 12g 1.00000
%12.3e 1.000e+00
%12e 1.000000e+00
%+12e +1.000000e+00
% 12e 1.000000e+00
%12.3f 1.000
%12f 1.000000
%+12f +1.000000
% 12f 1.000000
%12.3g 1
%12g 1
%+12g +1
% 12g 1
%#12.3g 1.00
%#12g 1.00000
%#+12g +1.00000
%# 12g 1.00000
%12.3g 1
%12g 1
%+12g +1
% 12g 1
%012.3e 0001.000e+00
%012e 1.000000e+00
%+012e +1.000000e+00
% 012e 1.000000e+00
%012.3f 00000001.000
%012f 00001.000000
%+012f +0001.000000
% 012f 0001.000000
%012.3g 000000000001
%012g 000000000001
%+012g +00000000001
% 012g 00000000001
%#012.3g 000000001.00
%#012g 000001.00000
%#+012g +00001.00000
%# 012g 00001.00000
%012.3g 000000000001
%012g 000000000001
%+012g +00000000001
% 012g 00000000001
以上、C99 printf と一致していることは確認した。
--- boost-spirit-karma-real_generators09.hpp 2013-06-04 16:57:46.000000000 +0900
+++ boost-spirit-karma-real_generators10.hpp 2013-06-04 16:57:46.000000000 +0900
@@ -13,14 +13,18 @@
namespace boost { namespace spirit { namespace karma {
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+ int Width = 0, int Align = -1, char Pad = ' ',
+ typename CE = unused_type, typename CC = unused_type>
struct scientific_real_policies : real_policies<T> {
+ typedef boost::mpl::int_<
+ karma::generator_properties::countingbuffer
+ > properties;
typedef CE CharEncoding;
typedef CC Tag;
template <typename Inserter, typename OutputIterator, typename Policies>
- static bool call(OutputIterator &sink, T n, Policies const &p)
+ static bool call_n(OutputIterator &sink, T n, Policies const &p, bool force_sign)
{
- bool force_sign = p.force_sign(n);
bool sign_val = false;
int flags = p.floatfield(n);
if (traits::test_negative(n)) {
@@ -81,6 +85,43 @@
return p.template exponent<CharEncoding, Tag>(sink, traits::truncate_to_long::call(dim));
return r;
}
+
+ template <typename Inserter, typename OutputIterator, typename Policies>
+ static bool call(OutputIterator &sink, T n, Policies const &p)
+ {
+ if (!Width)
+ return call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
+ bool r = false;
+ if (Align < 0) { // left align
+ karma::detail::enable_counting<OutputIterator> counting(sink);
+ r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
+ while (r && int(counting.count()) < Width)
+ r = karma::generate(sink, ' ');
+ }
+ else { // right align
+ bool sign_val = false;
+ if (Pad == '0' && traits::test_negative(n)) {
+ n = -n;
+ sign_val = true;
+ }
+ karma::detail::enable_buffering<OutputIterator> buffering(sink, Width);
+ {
+ karma::detail::disable_counting<OutputIterator> nocounting(sink);
+ r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, (Pad == '0' && p.force_sign(n)) ? false : p.force_sign(n));
+ }
+ buffering.disable();
+ karma::detail::enable_counting<OutputIterator> counting(sink, buffering.buffer_size());
+ if (sign_val || (Pad == '0' && p.force_sign(n)))
+ r = sign_inserter::call(sink, (force_sign && !sign_val && BlankSign) || traits::test_zero(n), sign_val, p.force_sign(n));
+ while (r && int(counting.count()) < Width)
+ r = karma::generate(sink, Pad);
+ if (r) {
+ buffering.buffer_copy();
+ }
+ }
+ return r;
+ }
+
static bool force_sign(T) { return ForceSign; }
static bool trailing_zeros(T) { return true; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
@@ -104,8 +145,10 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
-struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+ int Width = 0, int Align = -1, char Pad = ' ',
+ typename CE = unused_type, typename CC = unused_type>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
template <typename OutputIterator>
static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
@@ -115,8 +158,10 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
-struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+ int Width = 0, int Align = -1, char Pad = ' ',
+ typename CE = unused_type, typename CC = unused_type>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
static bool trailing_zeros(T) { return true; }
static int floatfield(T n)
{
@@ -136,8 +181,10 @@
}
};
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
-struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+ int Width = 0, int Align = -1, char Pad = ' ',
+ typename CE = unused_type, typename CC = unused_type>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
static bool trailing_zeros(T) { return false; }
static int floatfield(T n)
{
最後に全体像を示す。Boost Spirit Karma のサンプルは参考にしたが、実用的ではないので、以下のものでよいと思う。
/*
boost-spirit-karma-real_generators.hpp
Copyright (C) 2013 Taiji Yamada <taiji@aihara.co.jp>
Distributed under the Boost Software License, Version 1.0.
(See http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef _boost_spirit_karma_real_generators_hpp_
#define _boost_spirit_karma_real_generators_hpp_
#include <boost/spirit/include/karma.hpp>
namespace boost { namespace spirit { namespace karma {
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
int Width = 0, int Align = -1, char Pad = ' ',
typename CE = unused_type, typename CC = unused_type>
struct scientific_real_policies : real_policies<T> {
typedef boost::mpl::int_<
karma::generator_properties::countingbuffer
> properties;
typedef CE CharEncoding;
typedef CC Tag;
template <typename Inserter, typename OutputIterator, typename Policies>
static bool call_n(OutputIterator &sink, T n, Policies const &p, bool force_sign)
{
bool sign_val = false;
int flags = p.floatfield(n);
if (traits::test_negative(n)) {
n = -n;
sign_val = true;
}
unsigned precision = p.precision(n);
if (std::numeric_limits<T>::digits10) {
precision = (std::min)(precision,
(unsigned)std::numeric_limits<T>::digits10 + 1);
}
using namespace std;
T dim = 0;
if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n)) {
dim = log10(n);
if (dim > 0)
n /= spirit::traits::pow10<T>(traits::truncate_to_long::call(dim));
else if (n < 1.) {
long exp = traits::truncate_to_long::call(-dim);
if (exp != -dim)
++exp;
dim = -exp;
n *= spirit::traits::pow10<T>(exp);
}
}
T integer_part;
T precexp = spirit::traits::pow10<T>(precision);
T fractional_part = modf(n, &integer_part);
fractional_part = floor(fractional_part * precexp + T(0.5));
if (fractional_part >= precexp) {
fractional_part = floor(fractional_part - precexp);
integer_part += 1;
}
T long_int_part = floor(integer_part);
T long_frac_part = fractional_part;
unsigned prec = precision;
if (!p.trailing_zeros(n)) {
T frac_part_floor = long_frac_part;
if (0 != long_frac_part) {
while (0 != prec &&
0 == traits::remainder<10>::call(long_frac_part)) {
long_frac_part = traits::divide<10>::call(long_frac_part);
--prec;
}
}
else
prec = 0;
if (precision != prec)
long_frac_part = frac_part_floor / spirit::traits::pow10<T>(precision-prec);
}
if (false &&
sign_val && traits::test_zero(long_int_part) && traits::test_zero(long_frac_part))
sign_val = false;
bool r = p.integer_part(sink, long_int_part, sign_val, force_sign);
r = r && p.dot(sink, long_frac_part, precision);
r = r && p.fraction_part(sink, long_frac_part, prec, precision);
if (r && 0 == (Policies::fmtflags::fixed & flags))
return p.template exponent<CharEncoding, Tag>(sink, traits::truncate_to_long::call(dim));
return r;
}
template <typename Inserter, typename OutputIterator, typename Policies>
static bool call(OutputIterator &sink, T n, Policies const &p)
{
if (!Width)
return call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
bool r = false;
if (Align < 0) { // left align
karma::detail::enable_counting<OutputIterator> counting(sink);
r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
while (r && int(counting.count()) < Width)
r = karma::generate(sink, ' ');
}
else { // right align
bool sign_val = false;
if (Pad == '0' && traits::test_negative(n)) {
n = -n;
sign_val = true;
}
karma::detail::enable_buffering<OutputIterator> buffering(sink, Width);
{
karma::detail::disable_counting<OutputIterator> nocounting(sink);
r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, (Pad == '0' && p.force_sign(n)) ? false : p.force_sign(n));
}
buffering.disable();
karma::detail::enable_counting<OutputIterator> counting(sink, buffering.buffer_size());
if (sign_val || (Pad == '0' && p.force_sign(n)))
r = sign_inserter::call(sink, (force_sign && !sign_val && BlankSign) || traits::test_zero(n), sign_val, p.force_sign(n));
while (r && int(counting.count()) < Width)
r = karma::generate(sink, Pad);
if (r) {
buffering.buffer_copy();
}
}
return r;
}
static bool force_sign(T) { return ForceSign; }
static bool trailing_zeros(T) { return true; }
static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
static unsigned precision(T) { return Precision; }
template <typename OutputIterator>
static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
{
return sign_inserter::call(sink, (force_sign && !sign && BlankSign) || traits::test_zero(n), sign, force_sign) &&
int_inserter<10>::call(sink, n);
}
template <typename CharEncoding, typename Tag, typename OutputIterator>
static bool exponent(OutputIterator &sink, long n)
{
long abs_n = traits::get_absolute_value(n);
bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
sign_inserter::call(sink, /*traits::test_zero(n)*/false,
traits::test_negative(n), /*false*/true);
if (r && abs_n < 10) // the C99 Standard requires at least two digits in the exponent
r = char_inserter<CharEncoding, Tag>::call(sink, '0');
return r && int_inserter<10>::call(sink, abs_n);
}
};
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
int Width = 0, int Align = -1, char Pad = ' ',
typename CE = unused_type, typename CC = unused_type>
struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
template <typename OutputIterator>
static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
{
return sign_inserter::call(sink, force_sign && !sign && BlankSign, sign, force_sign) &&
int_inserter<10>::call(sink, n);
}
};
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
int Width = 0, int Align = -1, char Pad = ' ',
typename CE = unused_type, typename CC = unused_type>
struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
static bool trailing_zeros(T) { return true; }
static int floatfield(T n)
{
using namespace std;
T abs_n = traits::get_absolute_value(n);
return (!(floor(log10(abs_n)) < Precision) || abs_n < 1e-4) ?
real_policies<T>::fmtflags::scientific : real_policies<T>::fmtflags::fixed;
}
static unsigned precision(T n)
{
using namespace std;
T abs_n = traits::get_absolute_value(n), l;
if ((l = floor(log10(1./abs_n))) > 1)
return Precision + (l - 1);
l = floor(log10(abs_n));
return Precision > l ? Precision - (l + 1) : Precision - 1;
}
};
template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
int Width = 0, int Align = -1, char Pad = ' ',
typename CE = unused_type, typename CC = unused_type>
struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
static bool trailing_zeros(T) { return false; }
static int floatfield(T n)
{
if (traits::test_zero(n))
return real_policies<T>::fmtflags::fixed;
return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::floatfield(n);
}
template <typename OutputIterator>
static bool dot(OutputIterator &sink, T n, unsigned precision)
{
if (traits::test_zero(n))
return true;
return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::dot(sink, n, precision);
}
template <typename OutputIterator>
static bool fraction_part(OutputIterator &sink, T n, unsigned adjprec, unsigned precision)
{
if (traits::test_zero(n))
return true;
return fixed_real_policies<T, Precision, ForceSign, BlankSign>::fraction_part(sink, n, adjprec, precision);
}
};
} } }
#endif
Boost Karma による C99 printf int
さて、Karma における符号付き整数のジェネレータである int_generator<T, Radix=10, force_sign=false>
は、right_align(width, pad)
ディレクティブとの組み合わせなどで、ほとんどの書式が可能であるが、以下のような書式の実現は困難である。
%04lld 0001
%+04lld +001
% 04lld 001
そして、int_policies
のようなポリシークラスがあるわけではないので、書式を拡張するには int_generator
そのものを継承する必要がある。
よって、C99 printf 書式指定のような符号付き整数の表示を可能とする c99_int_generator
を用意してしまおう。これは以下のように使う。
#include <iostream>
#include <boost/range.hpp>
#include <boost/spirit/include/karma.hpp>
#include "boost-spirit-karma-int_generators.hpp"
namespace karma = boost::spirit::karma;
typedef karma::c99_int_generator<long long, 10, true, true, 6, 1, '0'> long_long_generator;
int main()
{
long_long_generator long_long;
long long v[] = { 0, 1, -100, };
std::cout << karma::format(long_long % karma::lit(','),
boost::make_iterator_range(v, v+sizeof(v)/sizeof(v[0]))) << std::endl;
return 0;
}
right_align(width, pad)
を使うとどうしても以下のようになってしまう表示が、
# karma::right_align(6, '0')[karma::int_generator<long long, 10, true>()]
0000 0,0000+1,00-100
以下のように表示できる。
00000, 00001,-00100
ここで使われているのは、以下の通りである。基本的には、浮動小数点でやったことと同様だ。
/*
boost-spirit-karma-int_generators.hpp
Copyright (C) 2013 Taiji Yamada <taiji@aihara.co.jp>
Distributed under the Boost Software License, Version 1.0.
(See http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef _boost_spirit_karma_int_generators_hpp_
#define _boost_spirit_karma_int_generators_hpp_
#include <boost/spirit/include/karma.hpp>
namespace boost { namespace spirit { namespace karma {
template <typename T, unsigned Radix = 10, bool ForceSign = false, bool BlankSign = false,
int Width = 0, int Align = -1, char Pad = ' ',
typename CharEncoding = unused_type, typename Tag = unused_type>
struct c99_int_generator : any_int_generator<T, CharEncoding, Tag, Radix, ForceSign> {
typedef boost::mpl::int_<
karma::generator_properties::countingbuffer
> properties;
template <typename OutputIterator, typename Attribute>
static bool insert_int(OutputIterator &sink, Attribute const &attr, bool force_sign = ForceSign)
{
return sign_inserter::call(sink, (force_sign && !traits::test_negative(attr) && BlankSign),
traits::test_negative(attr), force_sign) &&
int_inserter<Radix, CharEncoding, Tag>::call(sink, traits::get_absolute_value(attr));
}
template <typename OutputIterator, typename Context, typename Delimiter, typename Attribute>
static bool generate(OutputIterator &sink, Context &context, Delimiter const &d, Attribute const &attr)
{
if (!traits::has_optional_value(attr))
return false;
if (!Width)
return insert_int(sink, traits::extract_from<T>(attr, context), ForceSign) &&
delimit_out(sink, d);
bool r = false;
if (Align < 0) { // left align
karma::detail::enable_counting<OutputIterator> counting(sink);
r = insert_int(sink, traits::extract_from<T>(attr, context), ForceSign) &&
delimit_out(sink, d);
while (r && int(counting.count()) < Width)
r = karma::generate(sink, ' ');
}
else { // right align
bool sign_val = false;
Attribute n = attr;
if (Pad == '0' && traits::test_negative(n)) {
n = -n;
sign_val = true;
}
karma::detail::enable_buffering<OutputIterator> buffering(sink, Width);
{
karma::detail::disable_counting<OutputIterator> nocounting(sink);
r = insert_int(sink, traits::extract_from<T>(n, context), (Pad == '0' && ForceSign) ? false : ForceSign) &&
delimit_out(sink, d);
}
buffering.disable();
karma::detail::enable_counting<OutputIterator> counting(sink, buffering.buffer_size());
if (sign_val || (Pad == '0' && ForceSign))
r = sign_inserter::call(sink, (ForceSign && !sign_val && BlankSign), sign_val, ForceSign);
while (r && int(counting.count()) < Width)
r = karma::generate(sink, Pad);
if (r) {
buffering.buffer_copy();
}
}
return r;
}
};
} } }
#endif
符号無し整数ではこうした問題はない。