From patchwork Sat Nov 17 15:51:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Buclaw X-Patchwork-Id: 999323 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-490371-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=gdcproject.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="CajZ2nYZ"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42y02X6HzSz9s5c for ; Sun, 18 Nov 2018 02:51:31 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; q= dns; s=default; b=wVLRxw7uR0TXYxXk1Lr/MEQzxYET5YDLToBpLwe+cq1out hr4nLa+7T4ACHWw90TvzSsEK3dfDpHUS2yW6qOmI57xPO1JIIEY+CQq927ZnV5dW YunFzLwufqnRewd1bTZsktC5QT7MYjvWOkTk/CLoQMzjvI+KZn67gorLJQ4AM= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; s= default; bh=ItOiSI5vGqBHh9OZ+RKeICeZSzI=; b=CajZ2nYZK9uvr4YuWW3V DbrKy5QEst9UAucJKD2iL9ZCxSKYFqYoPEfAjUMQvRtRGTSk7TIfBV0n1J2bq0bo SLAWB0ct/3xzF9z8sBlxojElxTZYQR+OlR2oseRSfIVvG1637DjXkf7y7uI1hczl IZ6y+wXtpzAkNq4Ez5CizIw= Received: (qmail 85219 invoked by alias); 17 Nov 2018 15:51:22 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 85174 invoked by uid 89); 17 Nov 2018 15:51:19 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.4 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=sticky, Hex, hex, After X-HELO: mail-qk1-f177.google.com Received: from mail-qk1-f177.google.com (HELO mail-qk1-f177.google.com) (209.85.222.177) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 17 Nov 2018 15:51:15 +0000 Received: by mail-qk1-f177.google.com with SMTP id m5so42312594qka.9 for ; Sat, 17 Nov 2018 07:51:15 -0800 (PST) MIME-Version: 1.0 From: Iain Buclaw Date: Sat, 17 Nov 2018 16:51:02 +0100 Message-ID: Subject: [PATCH, libphobos] Committed IEEE quadruple support to std.conv To: gcc-patches X-IsSubscribed: yes This patch is backported from phobos 2.079, in continuation of finishing off AArch64 library support. Bootstrapped and ran D2 testsuite on x86_64-linux-gnu. Committed to trunk as r266238. diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 127849d0131..76ac5321382 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -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) - { - 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(); @@ -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")); + + // 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) @@ -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 (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"); @@ -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