mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 14:10:30 +03:00
std.format: Make formatting %f (and %g partly) 100% @nogc.
This commit is contained in:
parent
0cbeff86e9
commit
b6a51524f3
1 changed files with 54 additions and 130 deletions
|
@ -1786,9 +1786,7 @@ private auto printFloatF(bool g, Writer, T, Char)(return char[] buf, auto ref Wr
|
|||
if (is(T == float) || is(T == double)
|
||||
|| (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
|
||||
{
|
||||
import std.conv : to;
|
||||
import std.algorithm.comparison : max;
|
||||
import std.math : log10, abs, floor, ceil;
|
||||
import std.format.internal.write : writeAligned, PrecisionType;
|
||||
|
||||
enum int bias = T.max_exp - 1;
|
||||
|
||||
|
@ -1801,8 +1799,6 @@ if (is(T == float) || is(T == double)
|
|||
// special treatment for 0.0
|
||||
if (exp == 0 && mnt == 0)
|
||||
{
|
||||
import std.format.internal.write : writeAligned, PrecisionType;
|
||||
|
||||
writeAligned(w, sgn, "0", ".", "", f, PrecisionType.fractionalDigits);
|
||||
|
||||
return buf[0 .. 0];
|
||||
|
@ -1815,50 +1811,7 @@ if (is(T == float) || is(T == double)
|
|||
exp = 1;
|
||||
exp -= bias;
|
||||
|
||||
// estimate the number of bytes needed left and right of the decimal point
|
||||
// the speed of the algorithm depends on being as accurate as possible with
|
||||
// this estimate
|
||||
|
||||
// Default for the right side is the number of digits given by f.precision plus one for the dot.
|
||||
static if (g)
|
||||
auto max_right = max(0, f.precision - cast(int) val.abs.log10.floor + 1);
|
||||
else
|
||||
auto max_right = f.precision + 1;
|
||||
|
||||
// If the exponent is <= 0 there is only the sign and one digit left of the dot, else
|
||||
// we have to estimate the number of digits. The factor between exp, which is the number of
|
||||
// digits in binary system and the searched number is log_2(10). We round this down to 3.32 to
|
||||
// get a conservative estimate. We need to add 3, because of the sign, the fact, that the
|
||||
// logarithm is one to small and because we need to round up instead of down, which to!int does.
|
||||
static if (g)
|
||||
auto max_left = max(2, 2 + cast(int) val.abs.log10.ceil);
|
||||
else
|
||||
auto max_left = exp > 0 ? to!int(exp / 3.32) + 3 : 2;
|
||||
|
||||
// If the result is not left justified, we may need to add more digits here for getting the
|
||||
// correct width.
|
||||
if (!f.flDash)
|
||||
{
|
||||
static if (g)
|
||||
// %g cannot reduce the value by max_right due to trailing zeros, which are removed later
|
||||
max_left = max(max_left, f.width);
|
||||
else
|
||||
max_left = max(max_left, f.width - max_right + 2);
|
||||
}
|
||||
|
||||
// If the result is left justified, we may need to add more digits to the right. This strongly
|
||||
// depends, on the exponent, see above. This time, we need to be conservative in the other direction
|
||||
// for not missing a digit; therefore we round log_2(10) up to 3.33.
|
||||
if (exp > 0 && f.flDash)
|
||||
max_right = max(max_right, f.width - to!int(exp / 3.33) - 1);
|
||||
else if (f.flDash)
|
||||
max_right = max(max_right, f.width - 1);
|
||||
|
||||
size_t length = max_left + max_right;
|
||||
char[] buffer = length <= buf.length ? buf[0 .. length] : new char[length];
|
||||
size_t start = max_left;
|
||||
size_t left = max_left;
|
||||
size_t right = max_left;
|
||||
char[T.max_exp + T.mant_dig + 1] dec_buf;
|
||||
|
||||
// for rounding we need to know if the rest of the number is exactly 0, between 0 and 0.5, 0.5 or above 0.5
|
||||
enum roundType { ZERO, LOWER, FIVE, UPPER }
|
||||
|
@ -1924,9 +1877,17 @@ if (is(T == float) || is(T == double)
|
|||
enum max_buf = 18;
|
||||
}
|
||||
|
||||
size_t start = 2;
|
||||
size_t left = 2;
|
||||
size_t right = 2;
|
||||
|
||||
ulong[max_buf] bigbuf;
|
||||
if (exp >= T.mant_dig)
|
||||
{
|
||||
left = start = dec_buf.length - 1;
|
||||
right = dec_buf.length;
|
||||
dec_buf[start] = '.';
|
||||
|
||||
// large number without fractional digits
|
||||
//
|
||||
// As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits,
|
||||
|
@ -1969,13 +1930,9 @@ if (is(T == float) || is(T == double)
|
|||
if (mybig[msu] == 0)
|
||||
++msu;
|
||||
|
||||
buffer[--left] = cast(byte) ('0' + mod);
|
||||
dec_buf[--left] = cast(byte) ('0' + mod);
|
||||
}
|
||||
|
||||
if (f.precision > 0 || f.flHash) buffer[right++] = '.';
|
||||
|
||||
static if (g) start = left; // count precision from first digit
|
||||
|
||||
next = roundType.ZERO;
|
||||
}
|
||||
else if (exp < small_bound)
|
||||
|
@ -2019,9 +1976,8 @@ if (is(T == float) || is(T == double)
|
|||
mybig[0] = mnt << (upper - T.mant_dig);
|
||||
}
|
||||
|
||||
buffer[--left] = '0'; // 0 left of the dot
|
||||
|
||||
if (f.precision > 0 || f.flHash) buffer[right++] = '.';
|
||||
dec_buf[--left] = '0'; // 0 left of the dot
|
||||
dec_buf[right++] = '.';
|
||||
|
||||
static if (g)
|
||||
{
|
||||
|
@ -2046,17 +2002,19 @@ if (is(T == float) || is(T == double)
|
|||
if (mybig[lsu] == 0)
|
||||
++lsu;
|
||||
|
||||
buffer[right++] = cast(byte) ('0' + over);
|
||||
dec_buf[right++] = cast(byte) ('0' + over);
|
||||
|
||||
static if (g)
|
||||
{
|
||||
if (buffer[right - 1] != '0')
|
||||
if (dec_buf[right - 1] != '0')
|
||||
found = true;
|
||||
else if (!found)
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
static if (g) start = 2;
|
||||
|
||||
if (lsu >= count - 1 && mybig[count - 1] == 0)
|
||||
next = roundType.ZERO;
|
||||
else if (lsu == count - 1 && mybig[lsu] == 1L << 59)
|
||||
|
@ -2099,16 +2057,22 @@ if (is(T == float) || is(T == double)
|
|||
|
||||
// creating int part
|
||||
if (int_part == 0)
|
||||
buffer[--left] = '0';
|
||||
dec_buf[--left] = '0';
|
||||
else
|
||||
{
|
||||
import core.bitop : bsr;
|
||||
left = right = start = int_part.bsr * 100 / 332 + 4;
|
||||
|
||||
while (int_part > 0)
|
||||
{
|
||||
buffer[--left] = '0' + (int_part % 10);
|
||||
dec_buf[--left] = '0' + (int_part % 10);
|
||||
int_part /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (f.precision > 0 || f.flHash)
|
||||
buffer[right++] = '.';
|
||||
static if (g) size_t save_start = right;
|
||||
|
||||
dec_buf[right++] = '.';
|
||||
|
||||
// creating frac part
|
||||
static if (g) start = left + (found ? 0 : 1);
|
||||
|
@ -2128,11 +2092,11 @@ if (is(T == float) || is(T == double)
|
|||
tail &= (1L << tail_length) - 1;
|
||||
}
|
||||
}
|
||||
buffer[right++] = cast(byte)('0' + (frac_part >> (T.mant_dig - 1 - exp)));
|
||||
dec_buf[right++] = cast(byte)('0' + (frac_part >> (T.mant_dig - 1 - exp)));
|
||||
|
||||
static if (g)
|
||||
{
|
||||
if (buffer[right - 1] != '0')
|
||||
if (dec_buf[right - 1] != '0')
|
||||
found = true;
|
||||
else if (!found)
|
||||
start++;
|
||||
|
@ -2141,6 +2105,8 @@ if (is(T == float) || is(T == double)
|
|||
frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
|
||||
}
|
||||
|
||||
static if (g) start = save_start;
|
||||
|
||||
if (frac_part == 0)
|
||||
next = roundType.ZERO;
|
||||
else
|
||||
|
@ -2183,91 +2149,49 @@ if (is(T == float) || is(T == double)
|
|||
else
|
||||
{
|
||||
// Round to nearest, ties to even
|
||||
auto last = buffer[right - 1];
|
||||
if (last == '.') last = buffer[right - 2];
|
||||
auto last = dec_buf[right - 1];
|
||||
if (last == '.') last = dec_buf[right - 2];
|
||||
roundUp = last % 2 != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (f.precision > 0 || f.flHash)
|
||||
{
|
||||
// adding zeros
|
||||
buffer[right .. f.precision + start + 1] = '0';
|
||||
right = f.precision + start + 1;
|
||||
}
|
||||
|
||||
if (roundUp)
|
||||
{
|
||||
foreach_reverse (i;left .. right)
|
||||
{
|
||||
if (buffer[i] == '.') continue;
|
||||
if (buffer[i] == '9')
|
||||
buffer[i] = '0';
|
||||
if (dec_buf[i] == '.') continue;
|
||||
if (dec_buf[i] == '9')
|
||||
dec_buf[i] = '0';
|
||||
else
|
||||
{
|
||||
buffer[i]++;
|
||||
dec_buf[i]++;
|
||||
static if (g)
|
||||
{
|
||||
// in case of 0.0...009...9 => 0.0...010...0 we have to remove
|
||||
// the right most digit to get the precision right
|
||||
if (buffer[i] == '1')
|
||||
if (dec_buf[i] == '1')
|
||||
{
|
||||
foreach (j;left .. i)
|
||||
if (buffer[j] != '0' && buffer[j] != '.') goto printFloat_done;
|
||||
if (dec_buf[j] != '0' && dec_buf[j] != '.') goto printFloat_done;
|
||||
right--;
|
||||
}
|
||||
}
|
||||
goto printFloat_done;
|
||||
}
|
||||
}
|
||||
buffer[--left] = '1';
|
||||
dec_buf[--left] = '1';
|
||||
printFloat_done:
|
||||
}
|
||||
|
||||
while (right > start + 1 && dec_buf[right - 1] == '0') right--;
|
||||
|
||||
static if (g)
|
||||
{
|
||||
if (!f.flHash)
|
||||
{
|
||||
// removing trailing 0s
|
||||
while (right > left && buffer[right - 1]=='0')
|
||||
right--;
|
||||
if (right > left && buffer[right - 1]=='.')
|
||||
right--;
|
||||
}
|
||||
}
|
||||
writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.allDigits);
|
||||
else
|
||||
writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.fractionalDigits);
|
||||
|
||||
// sign and padding
|
||||
bool need_sgn = false;
|
||||
if (sgn != "")
|
||||
{
|
||||
// when padding with zeros we need to postpone adding the sign
|
||||
if (right - left < f.width && !f.flDash && f.flZero)
|
||||
need_sgn = true;
|
||||
else
|
||||
buffer[--left] = sgn[0];
|
||||
}
|
||||
|
||||
if (right - left < f.width)
|
||||
{
|
||||
if (f.flDash)
|
||||
{
|
||||
// padding right
|
||||
buffer[right .. f.width + left] = ' ';
|
||||
right = f.width + left;
|
||||
}
|
||||
else
|
||||
{
|
||||
// padding left
|
||||
buffer[right - f.width .. left] = f.flZero ? '0' : ' ';
|
||||
left = right - f.width;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_sgn)
|
||||
buffer[left] = sgn[0];
|
||||
|
||||
return buffer[left .. right];
|
||||
return buf[0 .. 0];
|
||||
}
|
||||
|
||||
@safe unittest
|
||||
|
@ -3256,26 +3180,26 @@ if (is(T == float) || is(T == double)
|
|||
|
||||
f.spec = 'f';
|
||||
f.precision = 15;
|
||||
assert(printFloat(buf[], w, cast(double) E, f) == "2.718281828459045");
|
||||
assert(printFloat(buf[], w, cast(double) E, f) == "");
|
||||
|
||||
f.precision = 20;
|
||||
assert(printFloat(buf[], w, double.max, f).length == 330);
|
||||
assert(printFloat(buf[], w, nextUp(0.0), f) == "0.00000000000000000000");
|
||||
assert(printFloat(buf[], w, double.max, f) == "");
|
||||
assert(printFloat(buf[], w, nextUp(0.0), f) == "");
|
||||
|
||||
f.precision = 498;
|
||||
assert(printFloat(buf[], w, 1.0, f).length == 500);
|
||||
f.precision = 1000;
|
||||
assert(printFloat(buf[], w, 1.0, f) == "");
|
||||
|
||||
f.spec = 'g';
|
||||
f.precision = 15;
|
||||
assert(printFloat(buf[], w, cast(double) E, f) == "2.71828182845905");
|
||||
assert(printFloat(buf[], w, cast(double) E, f) == "");
|
||||
|
||||
f.precision = 20;
|
||||
assert(printFloat(buf[], w, double.max, f) == "1.7976931348623157081e+308");
|
||||
assert(printFloat(buf[], w, nextUp(0.0), f) == "4.9406564584124654418e-324");
|
||||
|
||||
f.flHash = true;
|
||||
f.precision = 499;
|
||||
assert(printFloat(buf[], w, 1.0, f).length == 500);
|
||||
f.precision = 1000;
|
||||
assert(printFloat(buf[], w, 1.0, f) == "");
|
||||
|
||||
assert(GC.stats.usedSize == stats.usedSize);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue