libphobos: Add IEEE quadruple support for std.conv

Backport from upstream phobos 2.079 for AArch64.

Reviewed-on: https://github.com/dlang/phobos/pull/5965

From-SVN: r266238
This commit is contained in:
Iain Buclaw 2018-11-17 15:48:42 +00:00
parent d11be094c7
commit 5d11bfefc5
1 changed files with 124 additions and 258 deletions

View File

@ -2644,7 +2644,6 @@ Target parse(Target, Source)(ref Source source)
if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
isFloatingPoint!Target && !is(Target == enum))
{
import core.stdc.math : HUGE_VAL;
import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit;
import std.exception : enforce;
@ -2669,7 +2668,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
{
if (msg == null)
msg = "Floating point conversion error";
return new ConvException(text(msg, " for input \"", p, "\"."), fn, ln);
return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln);
}
@ -2684,29 +2683,24 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
enforce(!p.empty, bailOut());
if (toLower(p.front) == 'i')
goto case 'i';
enforce(!p.empty, bailOut());
break;
case '+':
p.popFront();
enforce(!p.empty, bailOut());
break;
case 'i': case 'I':
// inf
p.popFront();
enforce(!p.empty, bailOut());
if (toLower(p.front) == 'n')
{
p.popFront();
enforce(!p.empty, bailOut());
if (toLower(p.front) == 'f')
{
// 'inf'
p.popFront();
static if (isNarrowString!Source)
source = cast(Source) p;
return sign ? -Target.infinity : Target.infinity;
}
}
goto default;
enforce(!p.empty && toUpper(p.front) == 'N',
bailOut("error converting input to floating point"));
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'F',
bailOut("error converting input to floating point"));
// skip past the last 'f'
p.popFront();
static if (isNarrowString!Source)
source = cast(Source) p;
return sign ? -Target.infinity : Target.infinity;
default: {}
}
@ -2723,247 +2717,97 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
}
isHex = p.front == 'x' || p.front == 'X';
if (isHex) p.popFront();
}
else if (toLower(p.front) == 'n')
{
// nan
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'A',
bailOut("error converting input to floating point"));
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'N',
bailOut("error converting input to floating point"));
// skip past the last 'n'
p.popFront();
static if (isNarrowString!Source)
source = cast(Source) p;
return typeof(return).nan;
}
/*
* The following algorithm consists of 2 steps:
* 1) parseDigits processes the textual input into msdec and possibly
* lsdec/msscale variables, followed by the exponent parser which sets
* exp below.
* Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex
* and 000 is the exponent in decimal format with base 2.
* Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa
* in decimal and 000 is the exponent in decimal format with base 10.
* 2) Convert msdec/lsdec and exp into native real format
*/
real ldval = 0.0;
char dot = 0; /* if decimal point has been seen */
int exp = 0;
long msdec = 0, lsdec = 0;
ulong msdec = 0, lsdec = 0;
ulong msscale = 1;
bool sawDigits;
if (isHex)
enum { hex, decimal }
// sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits
void parseDigits(alias FloatFormat)()
{
int guard = 0;
int anydigits = 0;
uint ndigits = 0;
p.popFront();
while (!p.empty)
static if (FloatFormat == hex)
{
int i = p.front;
while (isHexDigit(i))
{
anydigits = 1;
i = isAlpha(i) ? ((i & ~0x20) - ('A' - 10)) : i - '0';
if (ndigits < 16)
{
msdec = msdec * 16 + i;
if (msdec)
ndigits++;
}
else if (ndigits == 16)
{
while (msdec >= 0)
{
exp--;
msdec <<= 1;
i <<= 1;
if (i & 0x10)
msdec |= 1;
}
guard = i << 4;
ndigits++;
exp += 4;
}
else
{
guard |= i;
exp += 4;
}
exp -= dot;
p.popFront();
if (p.empty)
break;
i = p.front;
if (i == '_')
{
p.popFront();
if (p.empty)
break;
i = p.front;
}
}
if (i == '.' && !dot)
{
p.popFront();
dot = 4;
}
else
break;
enum uint base = 16;
enum ulong msscaleMax = 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds
enum ubyte expIter = 4; // iterate the base-2 exponent by 4 for every hex digit
alias checkDigit = isHexDigit;
/*
* convert letter to binary representation: First clear bit
* to convert lower space chars to upperspace, then -('A'-10)
* converts letter A to 10, letter B to 11, ...
*/
alias convertDigit = (int x) => isAlpha(x) ? ((x & ~0x20) - ('A' - 10)) : x - '0';
sawDigits = false;
}
// Round up if (guard && (sticky || odd))
if (guard & 0x80 && (guard & 0x7F || msdec & 1))
else static if (FloatFormat == decimal)
{
msdec++;
if (msdec == 0) // overflow
{
msdec = 0x8000000000000000L;
exp++;
}
}
enforce(anydigits, bailOut());
enforce(!p.empty && (p.front == 'p' || p.front == 'P'),
bailOut("Floating point parsing: exponent is required"));
char sexp;
int e;
sexp = 0;
p.popFront();
if (!p.empty)
{
switch (p.front)
{
case '-': sexp++;
goto case;
case '+': p.popFront(); enforce(!p.empty,
new ConvException("Error converting input"~
" to floating point"));
break;
default: {}
}
}
ndigits = 0;
e = 0;
while (!p.empty && isDigit(p.front))
{
if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow
{
e = e * 10 + p.front - '0';
}
p.popFront();
ndigits = 1;
}
exp += (sexp) ? -e : e;
enforce(ndigits, new ConvException("Error converting input"~
" to floating point"));
static if (real.mant_dig == 64)
{
if (msdec)
{
int e2 = 0x3FFF + 63;
// left justify mantissa
while (msdec >= 0)
{
msdec <<= 1;
e2--;
}
// Stuff mantissa directly into real
()@trusted{ *cast(long*)&ldval = msdec; }();
()@trusted{ (cast(ushort*)&ldval)[4] = cast(ushort) e2; }();
import std.math : ldexp;
// Exponent is power of 2, not power of 10
ldval = ldexp(ldval,exp);
}
}
else static if (real.mant_dig == 53)
{
if (msdec)
{
//Exponent bias + 52:
//After shifting 52 times left, exp must be 1
int e2 = 0x3FF + 52;
// right justify mantissa
// first 11 bits must be zero, rest is implied bit + mantissa
// shift one time less, do rounding, shift again
while ((msdec & 0xFFC0_0000_0000_0000) != 0)
{
msdec = ((cast(ulong) msdec) >> 1);
e2++;
}
//Have to shift one more time
//and do rounding
if ((msdec & 0xFFE0_0000_0000_0000) != 0)
{
auto roundUp = (msdec & 0x1);
msdec = ((cast(ulong) msdec) >> 1);
e2++;
if (roundUp)
{
msdec += 1;
//If mantissa was 0b1111... and we added +1
//the mantissa should be 0b10000 (think of implicit bit)
//and the exponent increased
if ((msdec & 0x0020_0000_0000_0000) != 0)
{
msdec = 0x0010_0000_0000_0000;
e2++;
}
}
}
// left justify mantissa
// bit 11 must be 1
while ((msdec & 0x0010_0000_0000_0000) == 0)
{
msdec <<= 1;
e2--;
}
// Stuff mantissa directly into double
// (first including implicit bit)
()@trusted{ *cast(long *)&ldval = msdec; }();
//Store exponent, now overwriting implicit bit
()@trusted{ *cast(long *)&ldval &= 0x000F_FFFF_FFFF_FFFF; }();
()@trusted{ *cast(long *)&ldval |= ((e2 & 0xFFFUL) << 52); }();
import std.math : ldexp;
// Exponent is power of 2, not power of 10
ldval = ldexp(ldval,exp);
}
enum uint base = 10;
enum ulong msscaleMax = 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds
enum ubyte expIter = 1; // iterate the base-10 exponent once for every decimal digit
alias checkDigit = isDigit;
alias convertDigit = (int x) => x - '0';
// Used to enforce that any mantissa digits are present
sawDigits = startsWithZero;
}
else
static assert(false, "Floating point format of real type not supported");
goto L6;
}
else // not hex
{
if (toUpper(p.front) == 'N' && !startsWithZero)
{
// nan
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'A',
new ConvException("error converting input to floating point"));
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'N',
new ConvException("error converting input to floating point"));
// skip past the last 'n'
p.popFront();
static if (isNarrowString!Source)
source = cast(Source) p;
return typeof(return).nan;
}
bool sawDigits = startsWithZero;
static assert(false, "Unrecognized floating-point format used.");
while (!p.empty)
{
int i = p.front;
while (isDigit(i))
while (checkDigit(i))
{
sawDigits = true; /* must have at least 1 digit */
if (msdec < (0x7FFFFFFFFFFFL-10)/10)
msdec = msdec * 10 + (i - '0');
else if (msscale < (0xFFFFFFFF-10)/10)
i = convertDigit(i);
if (msdec < (ulong.max - base)/base)
{
lsdec = lsdec * 10 + (i - '0');
msscale *= 10;
// For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0
msdec = msdec * base + i;
}
else if (msscale < msscaleMax)
{
lsdec = lsdec * base + i;
msscale *= base;
}
else
{
exp++;
exp += expIter;
}
exp -= dot;
p.popFront();
@ -2981,21 +2825,29 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
if (i == '.' && !dot)
{
p.popFront();
dot++;
dot += expIter;
}
else
{
break;
}
}
enforce(sawDigits, new ConvException("no digits seen"));
}
if (!p.empty && (p.front == 'e' || p.front == 'E'))
{
char sexp;
int e;
sexp = 0;
// Have we seen any mantissa digits so far?
enforce(sawDigits, bailOut("no digits seen"));
static if (FloatFormat == hex)
enforce(!p.empty && (p.front == 'p' || p.front == 'P'),
bailOut("Floating point parsing: exponent is required"));
}
if (isHex)
parseDigits!hex;
else
parseDigits!decimal;
if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E')))
{
char sexp = 0;
int e = 0;
p.popFront();
enforce(!p.empty, new ConvException("Unexpected end of input"));
switch (p.front)
@ -3006,8 +2858,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
break;
default: {}
}
bool sawDigits = 0;
e = 0;
sawDigits = false;
while (!p.empty && isDigit(p.front))
{
if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow
@ -3015,7 +2866,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
e = e * 10 + p.front - '0';
}
p.popFront();
sawDigits = 1;
sawDigits = true;
}
exp += (sexp) ? -e : e;
enforce(sawDigits, new ConvException("No digits seen."));
@ -3024,7 +2875,14 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
ldval = msdec;
if (msscale != 1) /* if stuff was accumulated in lsdec */
ldval = ldval * msscale + lsdec;
if (ldval)
if (isHex)
{
import std.math : ldexp;
// Exponent is power of 2, not power of 10
ldval = ldexp(ldval,exp);
}
else if (ldval)
{
uint u = 0;
int pow = 4096;
@ -3051,10 +2909,10 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
u++;
}
}
L6: // if overflow occurred
enforce(ldval != HUGE_VAL, new ConvException("Range error"));
L1:
// if overflow occurred
enforce(ldval != real.infinity, new ConvException("Range error"));
static if (isNarrowString!Source)
source = cast(Source) p;
return sign ? -ldval : ldval;
@ -3232,15 +3090,20 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
{
import core.stdc.errno;
import core.stdc.stdlib;
import std.math : floatTraits, RealFormat;
errno = 0; // In case it was set by another unittest in a different module.
struct longdouble
{
static if (real.mant_dig == 64)
static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
{
ushort[8] value;
}
else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended)
{
ushort[5] value;
}
else static if (real.mant_dig == 53)
else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble)
{
ushort[4] value;
}
@ -3254,9 +3117,12 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
longdouble x1;
int i;
static if (real.mant_dig == 64)
static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
// Our parser is currently limited to ieeeExtended precision
enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
else static if (real.mant_dig == 53)
else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended)
enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble)
enum s = "0x1.FFFFFFFFFFFFFFFEp-1000";
else
static assert(false, "Floating point format for real not supported");
@ -3266,7 +3132,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
assert(s2.empty);
x = *cast(longdouble *)&ld;
static if (real.mant_dig == 64)
static if (floatTraits!real.realFormat == RealFormat.ieeeExtended)
{
version (CRuntime_Microsoft)
ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod