Merge changes to reals formatting from master

This commit is contained in:
Eugen Wissner 2017-11-28 09:32:20 +01:00
parent 3a24e9e462
commit c199cdd47c

View File

@ -17,11 +17,138 @@ module tanya.format;
import tanya.container.string;
import tanya.encoding.ascii;
public import tanya.format.conv;
import tanya.math;
import tanya.memory.op;
import tanya.meta.metafunction;
import tanya.meta.trait;
import tanya.range.array;
// Integer and floating point to string conversion is based on stb_sprintf
// written by Jeff Roberts.
// Returns the last part of buffer with converted number.
package(tanya) char[] integral2String(T)(T number, return ref char[21] buffer)
if (isIntegral!T)
{
// abs the integer.
ulong n64 = number < 0 ? -cast(long) number : number;
char* start = buffer[].ptr + buffer.sizeof - 1;
while (true)
{
// Do in 32-bit chunks (avoid lots of 64-bit divides even with constant
// denominators).
char* o = start - 8;
uint n;
if (n64 >= 100000000)
{
n = n64 % 100000000;
n64 /= 100000000;
}
else
{
n = cast(uint) n64;
n64 = 0;
}
while (n)
{
*--start = cast(char) (n % 10) + '0';
n /= 10;
}
// Ignore the leading zero if it was the last part of the integer.
if (n64 == 0)
{
if ((start[0] == '0')
&& (start != (buffer[].ptr + buffer.sizeof -1)))
{
++start;
}
break;
}
// Copy leading zeros if it wasn't the most significant part of the
// integer.
while (start != o)
{
*--start = '0';
}
}
// Get the length that we have copied.
uint l = cast(uint) ((buffer[].ptr + buffer.sizeof - 1) - start);
if (l == 0)
{
*--start = '0';
l = 1;
}
else if (number < 0) // Set the sign.
{
*--start = '-';
++l;
}
return buffer[$ - l - 1 .. $ - 1];
}
// Converting an integer to string.
@nogc nothrow pure @system unittest
{
char[21] buf;
assert(integral2String(80, buf) == "80");
assert(integral2String(-80, buf) == "-80");
assert(integral2String(0, buf) == "0");
assert(integral2String(uint.max, buf) == "4294967295");
assert(integral2String(int.min, buf) == "-2147483648");
}
/*
* Double-double high-precision floating point number.
*
* The first element is a base value corresponding to the nearest approximation
* of the target $(D_PSYMBOL HP) value, and the second element is an offset
* value corresponding to the difference between the target value and the base.
* Thus, the $(D_PSYMBOL HP) value represented is the sum of the base and the
* offset.
*/
private struct HP
{
private double base;
private double offset = 0.0;
private void normalize() @nogc nothrow pure @safe
{
const double target = this.base + this.offset;
this.offset -= target - this.base;
this.base = target;
}
private void multiply(ref const HP x, ref const HP y)
@nogc nothrow pure @safe
{
HP a, b;
long bt;
this.base = x.base * y.base;
copyFp(x.base, bt);
bt &= ulong.max << 27;
copyFp(bt, a.base);
a.offset = x.base - a.base;
copyFp(y.base, bt);
bt &= ulong.max << 27;
copyFp(bt, b.base);
b.offset = y.base - b.base;
this.offset = a.base * b.base - this.base
+ a.base * b.offset
+ a.offset * b.base
+ a.offset * b.offset;
this.offset += x.base * y.offset + x.offset * y.base;
}
}
private enum special = 0x7000;
private enum char period = '.';
@ -48,145 +175,121 @@ private static const ulong[20] powersOf10 = [
10000000000000000000UL,
];
private static const char[201] digitpair =
private static const char[201] digitPairs =
"0001020304050607080910111213141516171819202122232425262728293031323334353"
~ "6373839404142434445464748495051525354555657585960616263646566676869707172"
~ "737475767778798081828384858687888990919293949596979899";
private static const double[23] bottom = [
1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008,
1e+009, 1e+010, 1e+011, 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017,
1e+018, 1e+019, 1e+020, 1e+021, 1e+022,
private static const HP[23] bottom = [
HP(1e+000), HP(1e+001), HP(1e+002), HP(1e+003), HP(1e+004), HP(1e+005),
HP(1e+006), HP(1e+007), HP(1e+008), HP(1e+009), HP(1e+010), HP(1e+011),
HP(1e+012), HP(1e+013), HP(1e+014), HP(1e+015), HP(1e+016), HP(1e+017),
HP(1e+018), HP(1e+019), HP(1e+020), HP(1e+021), HP(1e+022),
];
private static const double[22] negativeBottom = [
1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009,
1e-010, 1e-011, 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018,
1e-019, 1e-020, 1e-021, 1e-022,
private static const HP[22] negativeBottom = [
HP(1e-001, -5.551115123125783e-018),
HP(1e-002, -2.0816681711721684e-019),
HP(1e-003, -2.0816681711721686e-020),
HP(1e-004, -4.7921736023859299e-021),
HP(1e-005, -8.1803053914031305e-022),
HP(1e-006, 4.5251888174113741e-023),
HP(1e-007, 4.5251888174113739e-024),
HP(1e-008, -2.0922560830128471e-025),
HP(1e-009, -6.2281591457779853e-026),
HP(1e-010, -3.6432197315497743e-027),
HP(1e-011, 6.0503030718060191e-028),
HP(1e-012, 2.0113352370744385e-029),
HP(1e-013, -3.0373745563400371e-030),
HP(1e-014, 1.1806906454401013e-032),
HP(1e-015, -7.7705399876661076e-032),
HP(1e-016, 2.0902213275965398e-033),
HP(1e-017, -7.1542424054621921e-034),
HP(1e-018, -7.1542424054621926e-035),
HP(1e-019, 2.4754073164739869e-036),
HP(1e-020, 5.4846728545790429e-037),
HP(1e-021, 9.2462547772103625e-038),
HP(1e-022, -4.8596774326570872e-039),
];
private static const double[13] top = [
1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207,
1e+230, 1e+253, 1e+276, 1e+299,
private static const HP[13] top = [
HP(1e+023, 8388608),
HP(1e+046, 6.8601809640529717e+028),
HP(1e+069, -7.253143638152921e+052),
HP(1e+092, -4.3377296974619174e+075),
HP(1e+115, -1.5559416129466825e+098),
HP(1e+138, -3.2841562489204913e+121),
HP(1e+161, -3.7745893248228135e+144),
HP(1e+184, -1.7356668416969134e+167),
HP(1e+207, -3.8893577551088374e+190),
HP(1e+230, -9.9566444326005119e+213),
HP(1e+253, 6.3641293062232429e+236),
HP(1e+276, -5.2069140800249813e+259),
HP(1e+299, -5.2504760255204387e+282),
];
private static const double[13] negativeTop = [
1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207,
1e-230, 1e-253, 1e-276, 1e-299,
];
private static const double[13] topError = [
8388608, 6.8601809640529717e+028, -7.253143638152921e+052,
-4.3377296974619174e+075, -1.5559416129466825e+098,
-3.2841562489204913e+121, -3.7745893248228135e+144,
-1.7356668416969134e+167, -3.8893577551088374e+190,
-9.9566444326005119e+213, 6.3641293062232429e+236,
-5.2069140800249813e+259, -5.2504760255204387e+282,
];
private static const double[22] negativeBottomError = [
-5.551115123125783e-018, -2.0816681711721684e-019,
-2.0816681711721686e-020, -4.7921736023859299e-021,
-8.1803053914031305e-022, 4.5251888174113741e-023,
4.5251888174113739e-024, -2.0922560830128471e-025,
-6.2281591457779853e-026, -3.6432197315497743e-027,
6.0503030718060191e-028, 2.0113352370744385e-029, -3.0373745563400371e-030,
1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033,
-7.1542424054621921e-034, -7.1542424054621926e-035,
2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038,
-4.8596774326570872e-039,
];
private static const double[13] negativeTopError = [
3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086,
1.1875228833981544e-109, -5.0644902316928607e-132,
-6.7156837247865426e-155, -2.812077463003139e-178,
-5.7778912386589953e-201, 7.4997100559334532e-224,
-4.6439668915134491e-247, -6.3691100762962136e-270,
-9.436808465446358e-293, // 8.0970921678014997e-317,
private static const HP[13] negativeTop = [
HP(1e-023, 3.9565301985100693e-040L),
HP(1e-046, -2.299904345391321e-063L),
HP(1e-069, 3.6506201437945798e-086L),
HP(1e-092, 1.1875228833981544e-109L),
HP(1e-115, -5.0644902316928607e-132L),
HP(1e-138, -6.7156837247865426e-155L),
HP(1e-161, -2.812077463003139e-178L),
HP(1e-184, -5.7778912386589953e-201L),
HP(1e-207, 7.4997100559334532e-224L),
HP(1e-230, -4.6439668915134491e-247L),
HP(1e-253, -6.3691100762962136e-270L),
HP(1e-276, -9.436808465446358e-293L),
HP(1e-299, 8.0970921678014997e-317L),
];
private enum ulong tenTo19th = 1000000000000000000UL;
private void ddmultlo(A, B, C, D, E, F)(ref A oh,
ref B ol,
ref C xh,
ref D xl,
ref E yh,
ref F yl)
{
ol = ol + (xh * yl + xl * yh);
}
private void ddmultlos(A, B, C, D)(ref A oh, ref B ol, ref C xh, ref D yl)
{
ol = ol + (xh * yl);
}
private void ddrenorm(T, U)(ref T oh, ref U ol)
{
double s;
s = oh + ol;
ol = ol - (s - oh);
oh = s;
}
// Power can be -323 to +350.
private void raise2Power10(double* ohi,
double* olo,
double d,
int power) @nogc nothrow pure
private HP raise2Power10(const HP value, int power)
@nogc nothrow pure @safe
{
double ph, pl;
HP result;
if ((power >= 0) && (power <= 22))
{
ddmulthi(ph, pl, d, bottom[power]);
result.multiply(value, bottom[power]);
}
else
{
int e, et, eb;
double p2h, p2l;
HP p2;
int e = power;
e = power;
if (power < 0)
{
e = -e;
}
et = (e * 0x2c9) >> 14; /* %23 */
int et = (e * 0x2c9) >> 14; // % 23
if (et > 13)
{
et = 13;
}
eb = e - (et * 23);
int eb = e - (et * 23);
ph = d;
pl = 0.0;
result = value;
if (power < 0)
{
if (eb)
if (eb != 0)
{
--eb;
ddmulthi(ph, pl, d, negativeBottom[eb]);
ddmultlos(ph, pl, d, negativeBottomError[eb]);
result.multiply(value, negativeBottom[eb]);
}
if (et)
{
ddrenorm(ph, pl);
result.normalize();
--et;
ddmulthi(p2h, p2l, ph, negativeTop[et]);
ddmultlo(p2h,
p2l,
ph,
pl,
negativeTop[et],
negativeTopError[et]);
ph = p2h;
pl = p2l;
p2.multiply(result, negativeTop[et]);
result = p2;
}
}
else
{
if (eb)
if (eb != 0)
{
e = eb;
if (eb > 22)
@ -194,168 +297,141 @@ private void raise2Power10(double* ohi,
eb = 22;
}
e -= eb;
ddmulthi(ph, pl, d, bottom[eb]);
result.multiply(value, bottom[eb]);
if (e)
{
ddrenorm(ph, pl);
ddmulthi(p2h, p2l, ph, bottom[e]);
ddmultlos(p2h, p2l, bottom[e], pl);
ph = p2h;
pl = p2l;
result.normalize();
p2.multiply(result, bottom[e]);
result = p2;
}
}
if (et)
if (et != 0)
{
ddrenorm(ph, pl);
result.normalize();
--et;
ddmulthi(p2h, p2l, ph, top[et]);
ddmultlo(p2h, p2l, ph, pl, top[et], topError[et]);
ph = p2h;
pl = p2l;
p2.multiply(result, top[et]);
result = p2;
}
}
}
ddrenorm(ph, pl);
*ohi = ph;
*olo = pl;
result.normalize();
return result;
}
/*
* Given a float value, returns the significant bits in bits, and the position
* of the decimal point in decimalPos. +/-Inf and NaN are specified by special
* values returned in the decimalPos parameter.
* fracDigits is absolute normally, but if you want from first significant
* digits (got %g), or in 0x80000000
* of the decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified
* by special values returned in the $(D_PARAM exponent). Sing bit is set in
* $(D_PARAM sign).
*/
private int real2String(ref const(char)* start,
ref uint len,
char* out_,
out int decimalPos,
double value,
uint fracDigits) @nogc nothrow pure
private const(char)[] real2String(double value,
ref char[512] buffer,
out int exponent,
out bool sign) @nogc nothrow pure @trusted
{
long bits = 0;
int e, tens;
long bits;
copyFp(value, bits);
double d = value;
copyFp(bits, d);
auto exponent = cast(int) ((bits >> 52) & 2047);
auto ng = cast(int) (bits >> 63);
if (ng)
exponent = (bits >> 52) & 0x7ff;
sign = signBit(value);
if (sign)
{
d = -d;
value = -value;
}
if (exponent == 2047) // Is NaN or inf?
if (exponent == 2047) // Is NaN or Inf?
{
start = (bits & (((cast(ulong) 1) << 52) - 1)) ? "NaN" : "Inf";
decimalPos = special;
len = 3;
return ng;
exponent = special;
return (bits & ((1UL << 52) - 1)) != 0 ? "NaN" : "Inf";
}
if (exponent == 0) // Is zero or denormal?
{
if ((bits << 1) == 0) // Do zero.
if ((bits << 1) == 0) // Zero.
{
decimalPos = 1;
start = out_;
out_[0] = '0';
len = 1;
return ng;
exponent = 1;
buffer[0] = '0';
return buffer[0 .. 1];
}
// Find the right exponent for denormals.
{
long v = (cast(ulong) 1) << 51;
while ((bits & v) == 0)
for (long cursor = 1UL << 51; (bits & cursor) == 0; cursor >>= 1)
{
--exponent;
v >>= 1;
}
}
}
// Find the decimal exponent as well as the decimal bits of the value.
{
double ph, pl;
// log10 estimate - very specifically tweaked to hit or undershoot by
// no more than 1 of log10 of all exponents 1..2046.
tens = exponent - 1023;
// "617 / 2048" and "1233 / 4096" are estimations for the common logarithm
// (log10) of 2. Multiplied by a binary number it tells how big the number
// is in decimals, so it translates the binary exponent into decimal
// format. The estimation is tweaked to hit or undershoot by no more than
// 1 of log10 of all exponents 1..2046.
int tens = exponent - 1023; // Bias.
if (tens < 0)
{
tens = (tens * 617) / 2048;
tens = tens * 617 / 2048;
}
else
{
tens = ((tens * 1233) / 4096) + 1;
tens = tens * 1233 / 4096 + 1;
}
// Move the significant bits into position and stick them into an int.
raise2Power10(&ph, &pl, d, 18 - tens);
HP p = raise2Power10(HP(value), 18 - tens);
// Get full as much precision from double-double as possible.
bits = cast(long) ph;
bits = cast(long) p.base;
double vh = cast(double) bits;
double ahi = (ph - vh);
double t = (ahi - ph);
double alo = (ph - (ahi - t)) - (vh + t);
bits += cast(long) (ahi + alo + pl);
auto a = HP(p.base - vh);
double t = a.base - p.base;
a.offset = p.base - a.base + t - vh - t;
bits += cast(long) (a.base + a.offset + p.offset);
// Check if we undershot.
if ((cast(ulong) bits) >= tenTo19th)
// Check if we undershot (bits >= 10 ^ 19).
if ((cast(ulong) bits) >= 1000000000000000000UL)
{
++tens;
}
}
// Now do the rounding in integer land.
if (fracDigits & 0x80000000)
{
fracDigits = (fracDigits & 0x7ffffff) + 1;
}
else
{
fracDigits = tens + fracDigits;
}
if ((fracDigits < 24))
{
enum uint fracDigits = 6;
uint dg = 1;
if (cast(ulong) bits >= powersOf10[9])
if ((cast(ulong) bits) >= powersOf10[9])
{
dg = 10;
}
while (cast(ulong) bits >= powersOf10[dg])
uint length;
while ((cast(ulong) bits) >= powersOf10[dg])
{
++dg;
if (dg == 20)
{
goto noround;
goto NoRound;
}
}
if (fracDigits < dg)
{
// Add 0.5 at the right position and round.
e = dg - fracDigits;
if (cast(uint) e >= 24)
length = dg - fracDigits;
if (length >= 24)
{
goto noround;
goto NoRound;
}
ulong r = powersOf10[e];
ulong r = powersOf10[length];
bits = bits + (r / 2);
if (cast(ulong) bits >= powersOf10[dg])
if ((cast(ulong) bits) >= powersOf10[dg])
{
++tens;
}
bits /= r;
}
noround:
}
NoRound:
// Kill long trailing runs of zeros.
if (bits)
{
uint n;
for (;;)
{
if (bits <= 0xffffffff)
@ -364,26 +440,26 @@ private int real2String(ref const(char)* start,
}
if (bits % 1000)
{
goto donez;
goto Zeroed;
}
bits /= 1000;
}
n = cast(uint) bits;
auto n = cast(uint) bits;
while ((n % 1000) == 0)
{
n /= 1000;
}
bits = n;
donez:
}
Zeroed:
// Convert to string.
out_ += 64;
e = 0;
for (;;)
auto result = buffer.ptr + 64;
length = 0;
while (true)
{
uint n;
char *o = out_ - 8;
char* o = result - 8;
// Do the conversion in chunks of U32s (avoid most 64-bit divides,
// worth it, constant denomiators be damned).
if (bits >= 100000000)
@ -398,35 +474,31 @@ private int real2String(ref const(char)* start,
}
while (n)
{
out_ -= 2;
*cast(ushort*) out_ = *cast(ushort*) &digitpair[(n % 100) * 2];
result -= 2;
*cast(ushort*) result = *cast(ushort*) &digitPairs[(n % 100) * 2];
n /= 100;
e += 2;
length += 2;
}
if (bits == 0)
{
if ((e) && (out_[0] == '0'))
if ((length != 0) && (result[0] == '0'))
{
++out_;
--e;
++result;
--length;
}
break;
}
while (out_ != o)
for (; result !is o; ++length, --result)
{
*--out_ = '0';
++e;
*result = '0';
}
}
decimalPos = tens;
start = out_;
len = e;
return ng;
exponent = tens;
return result[0 .. length];
}
private void leadSign(bool negative, ref char[8] sign)
@nogc nothrow pure
@nogc nothrow pure @safe
{
sign[0] = 0;
if (negative)
@ -436,13 +508,13 @@ private void leadSign(bool negative, ref char[8] sign)
}
}
// Copies d to bits w/ strict aliasing (this compiles to nothing on /Ox).
private void copyFp(T, U)(ref T dest, ref U src)
/*
* Copies double into long and back bitwise.
*/
private void copyFp(T, U)(ref const U src, ref T dest) @trusted
if (T.sizeof == U.sizeof)
{
for (int count = 0; count < 8; ++count)
{
(cast(char*) &dest)[count] = (cast(char*) &src)[count];
}
copy((&src)[0 .. 1], (&dest)[0 .. 1]);
}
private void ddmulthi(ref double oh,
@ -453,13 +525,13 @@ private void ddmulthi(ref double oh,
double ahi, bhi;
long bt;
oh = xh * yh;
copyFp(bt, xh);
copyFp(xh, bt);
bt &= ((~cast(ulong) 0) << 27);
copyFp(ahi, bt);
copyFp(bt, ahi);
double alo = xh - ahi;
copyFp(bt, yh);
copyFp(yh, bt);
bt &= ((~cast(ulong) 0) << 27);
copyFp(bhi, bt);
copyFp(bt, bhi);
double blo = yh - bhi;
ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo;
}
@ -467,25 +539,6 @@ private void ddmulthi(ref double oh,
package(tanya) String format(string fmt, Args...)(char[] buf, Args args)
{
String result;
char* bf = buf.ptr;
// Ok, we have a percent, read the modifiers first.
int precision = -1;
int tz = 0;
bool negative;
// Handle each replacement.
char[512] num; // Big enough for e308 (with commas) or e-307.
char[8] lead;
char[8] tail;
char *s;
uint l, n;
ulong n64;
double fv;
int decimalPos;
const(char)* sn;
static if (isSomeString!(Args[0])) // String
{
@ -504,28 +557,23 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args)
}
else static if (isFloatingPoint!(Args[0])) // Float
{
fv = args[0];
if (precision == -1)
{
precision = 6;
}
else if (precision == 0)
{
precision = 1; // Default is 6.
}
char[512] num; // Big enough for e308 (with commas) or e-307.
char[8] lead;
char[8] tail = 0;
char *s;
int precision = 6;
int tz;
bool negative;
char* bf = buf.ptr;
int decimalPos;
// Read the double into a string.
if (real2String(sn,
l,
num.ptr,
decimalPos,
fv,
(precision - 1) | 0x80000000))
{
negative = true;
}
auto fv = real2String(args[0], num, decimalPos, negative);
const(char)* sn = fv.ptr;
auto l = cast(uint) fv.length;
// Clamp the precision and delete extra zeros after clamp.
n = precision;
uint n = precision;
if (l > cast(uint) precision)
{
l = precision;
@ -570,7 +618,6 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args)
goto dofloatfromg;
doexpfromg:
tail[0] = 0;
leadSign(negative, lead);
if (decimalPos == special)
{
@ -628,7 +675,6 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args)
goto flt_lead;
dofloatfromg:
tail[0] = 0;
leadSign(negative, lead);
if (decimalPos == special)
{
@ -765,121 +811,7 @@ package(tanya) String format(string fmt, Args...)(char[] buf, Args args)
// Get the length that we copied.
l = cast(uint) (s - (num.ptr + 64));
s = num.ptr + 64;
}
else static if (isPointer!(Args[0])) // Pointer
{
// Get the number.
n64 = cast(size_t) args[0];
size_t position = num.length;
do // Write at least "0" if the pointer is null.
{
num[--position] = lowerHexDigits[cast(size_t) (n64 & 15)];
n64 >>= 4;
}
while (n64 != 0);
result.insertBack("0x");
result.insertBack(num[position .. $]);
}
else static if (isIntegral!(Args[0])) // Integer
{
// Get the integer and abs it.
static if (Args[0].sizeof == 8)
{
long k64 = args[0];
n64 = cast(ulong) k64;
static if (isSigned!(Args[0]))
{
if (k64 < 0)
{
n64 = cast(ulong) -k64;
negative = true;
}
}
}
else
{
int k = args[0];
n64 = cast(uint) k;
static if (isSigned!(Args[0]))
{
if (k < 0)
{
n64 = cast(uint) -k;
negative = true;
}
}
}
// Convert to string.
s = num.ptr + num.length;
l = 0;
for (;;)
{
// Do in 32-bit chunks (avoid lots of 64-bit divides even
// with constant denominators).
char *o = s - 8;
if (n64 >= 100000000)
{
n = cast(uint) (n64 % 100000000);
n64 /= 100000000;
}
else
{
n = cast(uint) n64;
n64 = 0;
}
while (n)
{
s -= 2;
*cast(ushort*) s = *cast(ushort*) &digitpair[(n % 100) * 2];
n /= 100;
}
while (n)
{
*--s = cast(char) (n % 10) + '0';
n /= 10;
}
if (n64 == 0)
{
if ((s[0] == '0') && (s != (num.ptr + num.length)))
{
++s;
}
break;
}
while (s != o)
{
*--s = '0';
}
}
tail[0] = 0;
leadSign(negative, lead);
// Get the length that we copied.
l = cast(uint) ((num.ptr + num.length) - s);
if (l == 0)
{
*--s = '0';
l = 1;
}
if (precision < 0)
{
precision = 0;
}
}
else
{
static assert(false);
}
if (result.length > 0)
{
return result;
}
scopy:
// Get fw=leading/trailing space, precision=leading zeros.
if (precision < cast(int) l)
@ -1004,6 +936,33 @@ scopy:
*bf = 0;
result = String(buf[0 .. cast(int) (bf - buf.ptr)]);
}
else static if (isPointer!(Args[0])) // Pointer
{
char[size_t.sizeof * 2] buffer;
size_t position = buffer.length;
auto address = cast(size_t) args[0];
do // Write at least "0" if the pointer is null.
{
buffer[--position] = lowerHexDigits[cast(size_t) (address & 15)];
address >>= 4;
}
while (address != 0);
result.insertBack("0x");
result.insertBack(buffer[position .. $]);
}
else static if (isIntegral!(Args[0])) // Integer
{
char[21] buffer;
result.insertBack(integral2String(args[0], buffer));
}
else
{
static assert(false);
}
return result;
}