Fix Issue 21853 - std.format: formatting real.max in CTFE fails

This commit is contained in:
berni44 2021-04-23 14:59:31 +02:00 committed by The Dlang Bot
parent 0881660baf
commit 110cb1a8be
2 changed files with 39 additions and 12 deletions

View file

@ -48,6 +48,7 @@ if (is(T == float) || is(T == double)
import std.math.rounding : floor;
import std.math.traits : isInfinity, isNaN;
import std.math.exponential : log2;
import std.math.operations : nextUp;
auto val2 = val;
@ -55,6 +56,8 @@ if (is(T == float) || is(T == double)
exp = 32767;
else if (abs(val2) < real.min_normal)
exp = 0;
else if (abs(val2) >= nextUp(real.max / 2))
exp = 32766;
else
exp = cast(int) (val2.abs.log2.floor() + 16383);
@ -63,22 +66,31 @@ if (is(T == float) || is(T == double)
// NaN or infinity
mnt = isNaN(val2) ? ((1L << 63) - 1) : 0;
}
else if (exp > 16382 + 64) // bias + bits of ulong
{
val2 /= 2.0L ^^ (exp - (16382 + 64));
mnt = (cast(ulong) abs(val2)) & ((1L << 63) - 1);
}
else
{
auto delta = 16382 + 64 - (exp == 0 ? 1 : exp); // -1 in case of subnormals
if (delta > 16383)
if (exp > 16382 + 64) // bias + bits of ulong
val2 /= 2.0L ^^ (exp - (16382 + 64));
else
{
// need two steps to avoid overflow
val2 *= 2.0L ^^ 16383;
delta -= 16383;
auto delta = 16382 + 64 - (exp == 0 ? 1 : exp); // -1 in case of subnormals
if (delta > 16383)
{
// need two steps to avoid overflow
val2 *= 2.0L ^^ 16383;
delta -= 16383;
}
val2 *= 2.0L ^^ delta;
}
val2 *= 2.0L ^^ delta;
mnt = (cast(ulong) abs(val2)) & ((1L << 63) - 1);
ulong tmp = cast(ulong) abs(val2);
if (exp != 32767 && exp > 0 && tmp <= ulong.max / 2)
{
// correction, due to log2(val2) being rounded up:
exp--;
val2 *= 2.0L;
tmp = cast(ulong) abs(val2);
}
mnt = tmp & ((1L << 63) - 1);
}
double d = cast(double) val2;

View file

@ -918,6 +918,21 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
}
}
// https://issues.dlang.org/show_bug.cgi?id=21853
@safe pure unittest
{
import std.math.exponential : log2;
// log2 is broken for x87-reals on some computers in CTFE
// the following test excludes these computers from the test
// (issue 21757)
enum test = cast(int) log2(3.05e2312L);
static if (real.mant_dig == 64 && test == 7681) // 80 bit reals
{
static assert(format!"%e"(real.max) == "1.189731e+4932");
}
}
// https://issues.dlang.org/show_bug.cgi?id=20536
@safe pure unittest
{