fformat.h
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C头文件 代码 · 共 353 行
H
353 行
#ifndef _FFORMAT_H
#define _FFORMAT_H
// Copyright (C) 1992 Scott Kirkwood (Queen's University, Ontario, Canada)
// Voice phone : (613) 531-2674, internet: kirkwood@qucis.queensu.ca
// This program is given as freeware with no warantees. Not liable for
// any loss or damage resulting from this code. Please acknowledge authorship.
// Note: Tabs in are size 4
#include <strstream.h>
#ifdef SIGNED_IMPLEMENTED
#define SIGNED signed
#else
#define SIGNED
#endif
#ifdef USE_LONG_DOUBLE
type long double ld;
#else
typedef double ld;
#endif
// Description
// ===========
//
// FFormat is a C++ class that is designed to output formatted
// floating point numbers (float, double, etc.). By formatted I mean:
// 1,234,567.8901 2345
//
// TFormat is a template version of FFormat (actually a subclass of
// FFormat) that is designed to be flexible enough to plug in your own
// extended numeric classes. For instance, I have successfully used
// g++'s Integer class and Fix class. I have also given a minimal
// example of a Money class where "Money" uses a long to store its
// value and divides the value by a 100 to get dollars and cents. It
// also show's that FFormat can handle funny leading characters like
// the dollar sign with no probs.
//
// OTHER FEATURES
// ==============
//
// FFormat can format to a specified total character width, if the
// number doesn't fit it will be divided down until it does and then
// the appropriate SI notation will be affixed. For instance, if
// you gave IFormat the number 1234567 and set the width to 6 you would
// get:
// 1,235K
//
// You can change these values if you prefer the computer science
// K = 1,024, for instance.
//
// IFormat will, by default, right adjust number if the ouput is smaller
// than the width passed. Where a width of 0 signifies variable width.
// For instance, here's 123 with a width of 5 (vertical bars for clarity)
// | 123|
//
// You can toggle whether to use SI postfixes, right adjusting, and
// whether separators are used.
//
// DEFAULTS
// ========
// putseps = true (use the separator characters)
// width = 0 (unlimited length of output)
// use_si = true (that is, if width set to something)
// right_adjust = true (numbers are right adjusted)
// sep = ',' (comma is integer separator)
// sep_width = 3 (123,456 etc. in groups of 3)
// deci_sep = ' ' (space is the fractional part separator)
// deci_width = 4 (.1234 5678 etc. in groups of 4)
// precision = 0 (unlimited)
// allowexp = 0 (do not use exponent notation)
//
// By default the postfixes are:
// Kilo 'K' 1E3 Milli 'm' 1E-3
// Mega 'M' 1E6 Micro 'u' 1E-6 (or the mu character)
// Giga 'G' 1E9 Nano 'n' 1E-9
// Tera 'T' 1E12 Pico 'p' 1E-12
// Penta 'P' 1E15 Femto 'f' 1E-15
// Exa 'E' 1E18 Atto 'a' 1E-18
//
// WIDTH
// =====
// When specifying a width to shoot for, FFormat goes into a different
// mode. In fact most of the code deals with the problems we get when
// a width is specified. Here's a synopsis of what happens.
// - If the string fits, great. Right adjust if necessary.
// else if it doesn't fit then
// - If the number is not tiny then
// - chop off as many decimal places as needed to make it fit.
// - If that doesn't work,
// - Divide the number by the minimum SI value to make it fit.
// else if the number is tiny (like 1E-10) then
// - chop off as many decimal places as needed to fit (but leave 1)
// - if that doesn't work,
// - Divide the number by the largest SI value (ex. micro) to make
// it fit.
//
// DYNAMIC ALLOCATION
// ==================
// The string pointer "work" is dynamically allocated (with new and
// delete). You can safely delete work if you need the space, just
// make sure that work points to NULL before you call Str() again.
// If you haven't deleted "work" then the space will be either a)
// re-used if the new number will fit, or b) deleted and a new (larger)
// block of memory allocated. It is better, therefore, to print your
// large numbers first and work down to your smaller numbers later to
// prevent heap fragmentation. Alternatively, use Str(1E100), for instace,
// to get FFormat to make a lot of space available.
//
// TO DO
// =====
// I guess some people would like even more formatting features. Like:
// - Aligning numbers at the decimal point.
// - Allowing a space between the SI postfix and the numbers.
// - Allowing limits with the postfix notation. i.e. allow the use of
// K for 1,000 but not the use of M or higher.
// - It could be smarter about using exponents.
// - Could eliminate one or both separators in order to squeeze the
// number in the width asked for.
// - Make it faster.
// - I wanted to use "locale.h" to figure out the standards for the
// system, but I found the functions didn't work very well. I think
// that someone with more experience with these problems should make
// a "locale" aware subclass of FFormat. (and send it to me!)
class FFormat {
protected:
int putseps; // Should we use a separator
int width; // Total output width.
int use_si; // Should we use SI postfix notations?
int right_adjust; // Should we right adjust
char sep; // Usually a comma
short sep_width; // Usually three
short deci_width; // Usually four
char deci_sep; // Usually a space
int precision; // Number of digits after the decimal point
char *work; // Output string, dynamically allocated.
int worklen; // Size of allocated string
int allowexp; // allow exponent notation.
int nodeci; // Don't show past decimal point
void Show();
void SetVars();
void getinfo(char *p, int &intlen, int &decilen, int &otherlen);
void getinfo(char *p, char *&end, int &intlen,
int &decilen, int &otherlen, char *&afterdot);
int estimate_length(int intlen, int decilen, int otherlen);
void PutCommas(char *end, int len, char *work);
void RightAdjust(char *, int);
protected:
static char ext[];
static char txe[];
static ld exp[];
static ld pxe[];
char *itoa(ld); // Make your own itoa...
public:
FFormat(); // Constructor
const char *Str(SIGNED int num) { return Str((ld)num); }
const char *Str(SIGNED long num) { return Str((ld)num); }
const char *Str(unsigned int num) { return Str((ld)num); }
const char *Str(unsigned long num){ return Str((ld)num); }
const char *Str(float num) { return Str((ld)num); }
const char *Str(char num) { return Str((ld)num); }
#ifdef USE_LONG_DOUBLE
const char *Str(double num) { return Str((ld)num); }
#endif
const char *Str(ld num);
void SetSepWidth(int i) { sep_width = i; }
void SetSep(char ch) { sep = ch; }
void SetDeciWidth(int i) { deci_width = i; }
void SetDeciSep(char ch) { deci_sep = ch; }
void SetWidth(int i) { width = i; }
void SetUseSeparators(int i) { putseps = i; }
void SetRightAdjust(int i) { right_adjust = i; }
void SetUseSI(int i) { use_si = i; }
void SetUseExp(int i) { allowexp = i; }
void SetPrecision(int i) { precision = i; }
void SetSI(int i, char c); // set big SI character (i=1->'K')
void SetSI(int i, ld val); // Set big SI value (i=1->1E3)
void SetIS(int i, char c); // set small SI character (i=1->'m')
void SetIS(int i, ld val); // Set small SI value (i=1->1E-3)
int SepWidth() { return sep_width; }
char Sep() { return sep; }
int DeciWidth() { return deci_width; }
char DeciSep() { return deci_sep; }
int Width() { return width; }
int UseSeparators() { return putseps; }
int RightAdjust() { return right_adjust; }
int Precision() { return precision; }
int UseSI() { return use_si; }
int UseExp() { return allowexp; }
};
#ifdef HAVE_TEMPLATES
// OK If we have templates, then let's make a template class.
// This template class allows you to ADD extra ability to Format.
// For instance, I have successfully used TFormat<Integer> from the
// GNU library of variable length integers. The class you are adding
// should have (the equivalent of):
//
// friend ostream& operator <<(ostream &, const YourClass &)
// friend YourClass operator /(const YourClass, const YourClass)
// friend void mod(const YourClass, const YourClass, YourClass &Result)
// friend int abs(const YourClass)
// friend void operator +=(YourClass, const int)
// operator int();
//
// defined and declared. (Of course you know that it doesn't have to
// be declared exactly as above)
// My experience is that the entire template definition/declaration
// must be included in the header file to work correctly. So here it is...
template <class T>
class TFormat : public FFormat {
protected:
char *itoa(T);
public:
const char *Str(T);
};
// This is a copy of the code contained in FFormat (pretty much).
template <class T>
const char *TFormat<T>::Str(T num) {
int as = 0;
int sa = 0;
int intlen; // Length of integer part
int decilen; // Length of decimal part
int otherlen; // everthing else (except \0)
int total_len; // Length with commas dots and \0
char *str;
T tnum = num;
int tprecision = precision;
int tnodeci = nodeci;
do {
str = itoa(num);
getinfo(str, intlen, decilen, otherlen);
total_len = estimate_length(intlen, decilen, otherlen);
if (as || sa)
total_len++;
if (width && total_len - 1 > width) {
if (abs(num) >= 1) { // big number
if (!nodeci) {
delete str;
int xtralen = decilen;
if (deci_width)
xtralen += (xtralen - 1) / deci_width;
if (width >= total_len - xtralen) {
precision = width - intlen - otherlen;
if (sep_width)
precision -= (intlen - 1) / sep_width;
if (deci_width)
precision -= (precision - 1) / deci_width;
if (precision < 0) {
precision = 0;
nodeci = 1;
}
}
else {
precision = 0;
nodeci = 1;
}
}
else if (use_si && as < 6) {
delete str;
num = tnum / (T)(exp[++as]);
}
else
break;
}
else { // number is small
int leadingzeroes = 0;
char *p = str;
while (*p && *p != '.')
p++;
p++;
while (*p && *p++ == '0')
leadingzeroes++;
if (deci_width)
decilen += (decilen - 1) / deci_width;
if (width - leadingzeroes > total_len - decilen) {
precision = decilen - leadingzeroes -
(total_len - width);
delete str;
}
else if (use_si && sa < 6) {
delete str;
num = tnum / (T)(pxe[++sa]);
}
else
break;
}
}
} while (width && total_len - 1 > width);
if (width >= total_len - 1) {
total_len = width + 1;
}
if (!work || total_len > worklen) {
if (work)
delete work;
work = new char [total_len];
worklen = total_len;
}
PutCommas(str, intlen, work);
if (as || sa) {
int len = strlen(work);
work[len] = as?ext[as]:txe[sa];
work[len + 1] = '\0';
}
if (width && right_adjust) {
RightAdjust(work, width);
}
nodeci = tnodeci;
precision = tprecision;
// assert(strlen(work) < worklen);
return work;
}
template <class T>
char *TFormat<T>::itoa(T num) {
ostrstream mys; // Dynamically allocated string.
if (!allowexp) {
mys.setf(ios::fixed);
}
else {
mys.setf(ios::scientific);
}
mys.precision(precision);
#ifdef TFORMAT_ROUND
if (nodeci) { // compensate for lack of rounding
T result;
mod(num, num, result);
if (10 * result >= 5)
num += 1;
}
#endif
mys << num;
mys << ends;
return mys.str();
}
#endif
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?