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:
parent
d11be094c7
commit
5d11bfefc5
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue