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;
{
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);
}
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: {}
}
}
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)
- {
- int guard = 0;
- int anydigits = 0;
- uint ndigits = 0;
-
- p.popFront();
- while (!p.empty)
- {
- 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;
- }
-
- // Round up if (guard && (sticky || odd))
- if (guard & 0x80 && (guard & 0x7F || msdec & 1))
- {
- 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"));
+ enum { hex, decimal }
- static if (real.mant_dig == 64)
+ // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits
+ void parseDigits(alias FloatFormat)()
+ {
+ static if (FloatFormat == hex)
{
- 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);
- }
+ 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;
}
- else static if (real.mant_dig == 53)
+ else static if (FloatFormat == decimal)
{
- 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)
+ {
+ // 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 * 10 + (i - '0');
- msscale *= 10;
+ lsdec = lsdec * base + i;
+ msscale *= base;
}
else
{
- exp++;
+ exp += expIter;
}
exp -= dot;
p.popFront();
if (i == '.' && !dot)
{
p.popFront();
- dot++;
+ dot += expIter;
}
else
- {
break;
- }
}
- enforce(sawDigits, new ConvException("no digits seen"));
+
+ // 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 (!p.empty && (p.front == 'e' || p.front == 'E'))
+
+ if (isHex)
+ parseDigits!hex;
+ else
+ parseDigits!decimal;
+
+ if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E')))
{
- char sexp;
- int e;
+ char sexp = 0;
+ int e = 0;
- sexp = 0;
p.popFront();
enforce(!p.empty, new ConvException("Unexpected end of input"));
switch (p.front)
break;
default: {}
}
- bool sawDigits = 0;
- e = 0;
+ sawDigits = false;
while (!p.empty && isDigit(p.front))
{
if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow
e = e * 10 + p.front - '0';
}
p.popFront();
- sawDigits = 1;
+ sawDigits = true;
}
exp += (sexp) ? -e : e;
enforce(sawDigits, new ConvException("No digits seen."));
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;
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;
{
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;
}
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 (floatTraits!real.realFormat == RealFormat.ieeeExtended)
enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
- else static if (real.mant_dig == 53)
+ else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble)
enum s = "0x1.FFFFFFFFFFFFFFFEp-1000";
else
static assert(false, "Floating point format for real not supported");
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