Browse Source

Implement plFormat for floating-point types

Michael Hansen 10 years ago
parent
commit
d15f1557b6
  1. 92
      Sources/Plasma/CoreLib/plFormat.cpp
  2. 13
      Sources/Plasma/CoreLib/plFormat.h

92
Sources/Plasma/CoreLib/plFormat.cpp

@ -147,7 +147,7 @@ namespace plFormat_Private
case '5': case '6': case '7': case '8': case '9': case '5': case '6': case '7': case '8': case '9':
{ {
char *end = nullptr; char *end = nullptr;
spec.fPrecisionLeft = strtol(ptr, &end, 10); spec.fMinimumLength = strtol(ptr, &end, 10);
ptr = end - 1; ptr = end - 1;
} }
break; break;
@ -155,7 +155,7 @@ namespace plFormat_Private
{ {
hsAssert(*(ptr + 1), "Unterminated format specifier"); hsAssert(*(ptr + 1), "Unterminated format specifier");
char *end = nullptr; char *end = nullptr;
spec.fPrecisionRight = strtol(ptr + 1, &end, 10); spec.fPrecision = strtol(ptr + 1, &end, 10);
ptr = end - 1; ptr = end - 1;
} }
break; break;
@ -227,16 +227,16 @@ static plStringBuffer<char> _formatNumeric(const plFormat_Private::FormatSpec &f
max = 1; max = 1;
plStringBuffer<char> buffer; plStringBuffer<char> buffer;
if (format.fPrecisionLeft > max) { if (format.fMinimumLength > max) {
char *output = buffer.CreateWritableBuffer(format.fPrecisionLeft); char *output = buffer.CreateWritableBuffer(format.fMinimumLength);
memset(output, pad, format.fPrecisionLeft); memset(output, pad, format.fMinimumLength);
if (format.fAlignment == plFormat_Private::kAlignLeft) { if (format.fAlignment == plFormat_Private::kAlignLeft) {
_IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase); _IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase);
} else { } else {
_IFormatNumeric_Impl<_IType>(output + format.fPrecisionLeft, _IFormatNumeric_Impl<_IType>(output + format.fMinimumLength,
value, radix, upperCase); value, radix, upperCase);
} }
output[format.fPrecisionLeft] = 0; output[format.fMinimumLength] = 0;
} else { } else {
char *output = buffer.CreateWritableBuffer(max); char *output = buffer.CreateWritableBuffer(max);
_IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase); _IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase);
@ -268,21 +268,21 @@ static plStringBuffer<char> _formatDecimal(const plFormat_Private::FormatSpec &f
plStringBuffer<char> buffer; plStringBuffer<char> buffer;
char *output; char *output;
if (format.fPrecisionLeft > max) { if (format.fMinimumLength > max) {
output = buffer.CreateWritableBuffer(format.fPrecisionLeft); output = buffer.CreateWritableBuffer(format.fMinimumLength);
memset(output, pad, format.fPrecisionLeft); memset(output, pad, format.fMinimumLength);
if (format.fAlignment == plFormat_Private::kAlignLeft) if (format.fAlignment == plFormat_Private::kAlignLeft)
_IFormatNumeric_Impl<_IType>(output + max, abs, 10); _IFormatNumeric_Impl<_IType>(output + max, abs, 10);
else else
_IFormatNumeric_Impl<_IType>(output + format.fPrecisionLeft, abs, 10); _IFormatNumeric_Impl<_IType>(output + format.fMinimumLength, abs, 10);
output[format.fPrecisionLeft] = 0; output[format.fMinimumLength] = 0;
} else { } else {
output = buffer.CreateWritableBuffer(max); output = buffer.CreateWritableBuffer(max);
_IFormatNumeric_Impl<_IType>(output + max, abs, 10); _IFormatNumeric_Impl<_IType>(output + max, abs, 10);
output[max] = 0; output[max] = 0;
} }
int signPos = format.fPrecisionLeft - static_cast<int>(max); int signPos = format.fPrecision - static_cast<int>(max);
if (signPos < 0) if (signPos < 0)
signPos = 0; signPos = 0;
@ -296,7 +296,7 @@ static plStringBuffer<char> _formatDecimal(const plFormat_Private::FormatSpec &f
static plStringBuffer<char> _formatChar(const plFormat_Private::FormatSpec &format, int ch) static plStringBuffer<char> _formatChar(const plFormat_Private::FormatSpec &format, int ch)
{ {
hsAssert(format.fPrecisionLeft == 0 && format.fPadChar == 0, hsAssert(format.fMinimumLength == 0 && format.fPadChar == 0,
"Char formatting does not currently support padding"); "Char formatting does not currently support padding");
// Don't need to nul-terminate this, since plStringBuffer's constructor fixes it // Don't need to nul-terminate this, since plStringBuffer's constructor fixes it
@ -392,6 +392,60 @@ _PL_FORMAT_IMPL_INT_TYPE(long, unsigned long)
_PL_FORMAT_IMPL_INT_TYPE(int64_t, uint64_t) _PL_FORMAT_IMPL_INT_TYPE(int64_t, uint64_t)
#endif #endif
PL_FORMAT_IMPL(float)
{
return PL_FORMAT_FORWARD(format, double(value));
}
PL_FORMAT_IMPL(double)
{
char pad = format.fPadChar ? format.fPadChar : ' ';
// Cheating a bit here -- just pass it along to cstdio
char format_buffer[32];
size_t end = 0;
format_buffer[end++] = '%';
if (format.fPrecision) {
int count = snprintf(format_buffer + end, arrsize(format_buffer) - end,
".%d", format.fPrecision);
// Ensure one more space (excluding \0) is available for the format specifier
hsAssert(count > 0 && count + end + 2 < arrsize(format_buffer),
"Not enough space for format string");
end += count;
}
format_buffer[end++] =
(format.fFloatClass == plFormat_Private::kFloatExp) ? 'e' :
(format.fFloatClass == plFormat_Private::kFloatExpUpper) ? 'E' :
(format.fFloatClass == plFormat_Private::kFloatFixed) ? 'f' : 'g';
format_buffer[end] = 0;
int format_size = snprintf(nullptr, 0, format_buffer, value);
hsAssert(format_size > 0, "Your libc doesn't support reporting format size");
plStringBuffer<char> out_buffer;
char *output;
if (format.fMinimumLength > format_size) {
output = out_buffer.CreateWritableBuffer(format.fMinimumLength);
memset(output, pad, format.fMinimumLength);
if (format.fAlignment == plFormat_Private::kAlignLeft) {
snprintf(output, format_size + 1, format_buffer, value);
output[format_size] = pad; // snprintf overwrites this
output[format.fMinimumLength] = 0;
} else {
snprintf(output + (format.fMinimumLength - format_size), format_size + 1,
format_buffer, value);
}
} else {
output = out_buffer.CreateWritableBuffer(format_size);
snprintf(output, format_size + 1, format_buffer, value);
}
return out_buffer;
}
PL_FORMAT_IMPL(char) PL_FORMAT_IMPL(char)
{ {
/* Note: The use of unsigned here is not a typo -- we only format decimal /* Note: The use of unsigned here is not a typo -- we only format decimal
@ -445,18 +499,18 @@ static plStringBuffer<char> _formatString(const plFormat_Private::FormatSpec &fo
{ {
char pad = format.fPadChar ? format.fPadChar : ' '; char pad = format.fPadChar ? format.fPadChar : ' ';
if (format.fPrecisionLeft > value.GetSize()) { if (format.fMinimumLength > value.GetSize()) {
plStringBuffer<char> buf; plStringBuffer<char> buf;
char *output = buf.CreateWritableBuffer(format.fPrecisionLeft); char *output = buf.CreateWritableBuffer(format.fMinimumLength);
memset(output, pad, format.fPrecisionLeft); memset(output, pad, format.fMinimumLength);
if (format.fAlignment == plFormat_Private::kAlignRight) { if (format.fAlignment == plFormat_Private::kAlignRight) {
memcpy(output + (format.fPrecisionLeft - value.GetSize()), memcpy(output + (format.fMinimumLength - value.GetSize()),
value.GetData(), value.GetSize()); value.GetData(), value.GetSize());
} else { } else {
memcpy(output, value.GetData(), value.GetSize()); memcpy(output, value.GetData(), value.GetSize());
} }
output[format.fPrecisionLeft] = 0; output[format.fMinimumLength] = 0;
return buf; return buf;
} }

13
Sources/Plasma/CoreLib/plFormat.h

@ -73,7 +73,7 @@ Mead, WA 99021
* `b` | Binary * `b` | Binary
* `d` | Decimal (default) -- when used with char types, outputs a number instead of the UTF representation of the char * `d` | Decimal (default) -- when used with char types, outputs a number instead of the UTF representation of the char
* `c` | UTF character (default for character types) * `c` | UTF character (default for character types)
* `FFF.EEE` | Use FFF.EEE floating point precision * `.EEE` | Use EEE digits of floating point precision
* `f` | Fixed floating point format (ddd.ddd) * `f` | Fixed floating point format (ddd.ddd)
* `e` | Exponent notation for floating point (d.ddde[+/-]dd) * `e` | Exponent notation for floating point (d.ddde[+/-]dd)
* `E` | Same as 'e' format, but with upper case E (d.dddE[+/-]dd) * `E` | Same as 'e' format, but with upper case E (d.dddE[+/-]dd)
@ -114,8 +114,8 @@ namespace plFormat_Private
/** Represents a parsed format tag, for use in formatter implementations. */ /** Represents a parsed format tag, for use in formatter implementations. */
struct FormatSpec struct FormatSpec
{ {
int fPrecisionLeft = 0; /**< Requested padding and/or precision */ int fMinimumLength = 0; /**< Requested minimum padding length */
int fPrecisionRight = 0; /**< Requested precision after the . for floating-point */ int fPrecision = 0; /**< Requested precision for floating-point */
char fPadChar = 0; /**< Explicit padding char (default is space) */ char fPadChar = 0; /**< Explicit padding char (default is space) */
Alignment fAlignment = kAlignDefault; /**< Requested pad alignment */ Alignment fAlignment = kAlignDefault; /**< Requested pad alignment */
@ -192,6 +192,10 @@ namespace plFormat_Private
PL_FORMAT_TYPE(int64_t) PL_FORMAT_TYPE(int64_t)
PL_FORMAT_TYPE(uint64_t) PL_FORMAT_TYPE(uint64_t)
#endif #endif
PL_FORMAT_TYPE(float)
PL_FORMAT_TYPE(double)
PL_FORMAT_TYPE(const char *) PL_FORMAT_TYPE(const char *)
PL_FORMAT_TYPE(const wchar_t *) PL_FORMAT_TYPE(const wchar_t *)
PL_FORMAT_TYPE(const plString &) PL_FORMAT_TYPE(const plString &)
@ -203,9 +207,6 @@ namespace plFormat_Private
PL_FORMAT_TYPE(const std::string &) PL_FORMAT_TYPE(const std::string &)
PL_FORMAT_TYPE(const std::wstring &) PL_FORMAT_TYPE(const std::wstring &)
// TODO: Implement floating point types (float, double). They're harder
// than the others, so I'll get around to them later >.>
// Formats as "true" or "false", following normal string formatting rules. // Formats as "true" or "false", following normal string formatting rules.
// To use other formats, don't pass us a bool directly... // To use other formats, don't pass us a bool directly...
PL_FORMAT_TYPE(bool) PL_FORMAT_TYPE(bool)

Loading…
Cancel
Save