summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2018-07-01 10:47:05 +0200
committerEugen Wissner <belka@caraus.de>2018-07-02 10:47:05 +0200
commita332d727afca21195bb28008a78328514dd914d7 (patch)
treef4311dc841f963beed62ca4f77fabbddc5d51af3
parent8241943a58a2b4c0e6c3e0112d08f950ff6465d4 (diff)
downloadtanya-a332d727afca21195bb28008a78328514dd914d7.tar.gz
Implement errol2 for floating point formatting
Handles whole floating point numbers in the range between 2^54 and 2^131.
-rw-r--r--source/tanya/format.d155
1 files changed, 147 insertions, 8 deletions
diff --git a/source/tanya/format.d b/source/tanya/format.d
index 4336f23..5ccb8ce 100644
--- a/source/tanya/format.d
+++ b/source/tanya/format.d
@@ -843,17 +843,13 @@ private char[] errol1(const double value,
const double factor = t.base / (2.0 + epsilon);
// Upper narrow boundary
- FloatBits!double neighbour = { value };
- --neighbour.integral;
auto nMinus = HP(scaledInput.base, scaledInput.offset
- + (neighbour.floating - value) * factor);
+ + (previous(value) - value) * factor);
nMinus.normalize();
// Lower narrow boundary
- neighbour.floating = value;
- ++neighbour.integral;
auto nPlus = HP(scaledInput.base, scaledInput.offset
- + (neighbour.floating - value) * factor);
+ + (next(value) - value) * factor);
nPlus.normalize();
// Phase 3: Exponent Rectification
@@ -911,7 +907,7 @@ private char[] errol1(const double value,
@nogc nothrow pure @safe unittest
{
- char[512] buf;
+ char[512] buf;
int e;
assert(errol1(18.51234334, buf, e) == "1851234334");
@@ -1214,6 +1210,142 @@ private struct uint128
}
}
+private double next(const double value) @nogc nothrow pure @safe
+{
+ FloatBits!double bits = { floating: value };
+ ++bits.integral;
+ return bits.floating;
+}
+
+private double previous(const double value) @nogc nothrow pure @safe
+{
+ FloatBits!double bits = { floating: value };
+ --bits.integral;
+ return bits.floating;
+}
+
+private uint128 raise2ToExp(double value) @nogc nothrow pure @safe
+{
+ FloatBits!double bits = { floating: value };
+
+ return uint128(1) << ((bits.integral >> 52) - 1023);
+}
+
+private int indexMismatch(ulong low, ulong high) @nogc nothrow pure @safe
+{
+ enum ulong power10 = 10000000000U;
+ const ulong a = low / power10;
+ const ulong b = high / power10;
+ int index;
+
+ if (a != b)
+ {
+ index = 10;
+ low = a;
+ high = b;
+ }
+
+ for (;; ++index)
+ {
+ low /= 10;
+ high /= 10;
+
+ if (low == high)
+ {
+ return index;
+ }
+ }
+}
+
+private char[] errol2(double value,
+ return ref char[512] buffer,
+ out int exponent) @nogc nothrow pure @safe
+in
+{
+ assert(value > 9.007199254740992e15 && value < 3.40282366920938e38);
+}
+do
+{
+ auto v = uint128(value);
+ auto leftBoundary = v + raise2ToExp((value - previous(value)) / 2.0);
+ auto rightBoundary = v - raise2ToExp((next(value) - value) / 2.0);
+ FloatBits!double bits = { floating: value };
+
+ if (bits.integral & 0x1)
+ {
+ --leftBoundary;
+ }
+ else
+ {
+ --rightBoundary;
+ }
+
+ enum ulong power19 = cast(ulong) 1e19;
+
+ auto qr = leftBoundary.divMod(power19);
+ auto low = cast(ulong) qr[1];
+ const lowFactor = cast(ulong) (qr[0] % power19);
+
+ qr = rightBoundary.divMod(power19);
+ auto high = cast(ulong) qr[1];
+ const highFactor = cast(ulong) (qr[0] % power19);
+ size_t digitIndex;
+
+ if (lowFactor != highFactor)
+ {
+ low = lowFactor;
+ high = highFactor;
+ v = v / cast(ulong) 1e18;
+ }
+ else
+ {
+ digitIndex = 1;
+ }
+
+ int mismatch = indexMismatch(low, high);
+ ulong tens = 1;
+ for (; digitIndex < mismatch; ++digitIndex)
+ {
+ tens *= 10;
+ }
+ const midpoint = cast(ulong) (v / tens);
+
+ if (lowFactor != highFactor)
+ {
+ mismatch += 19;
+ }
+
+ char[21] intBuffer;
+ auto intSlice = integral2String(midpoint, intBuffer);
+
+ if (mismatch != 0)
+ {
+ if (intSlice[$ - 1] >= '5')
+ {
+ ++intSlice[$ - 2];
+ }
+ intSlice.popBack();
+ }
+ const begin = buffer.length - intSlice.length;
+ copy(intSlice, buffer[begin .. $]);
+
+ exponent = cast(int) (intSlice.length + mismatch);
+
+ return buffer[begin .. $];
+}
+
+@nogc nothrow pure @safe unittest
+{
+ char[512] buf;
+ int e;
+
+ assert(errol2(9.007199254740994e15, buf, e) == "9007199254740994");
+ assert(e == 16);
+
+ assert(errol2(9.007199254740994e25, buf, e) == "9007199254740994");
+ assert(e == 26);
+}
+
/*
* Given a float value, returns the significant bits, and the position of the
* decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified by
@@ -1254,7 +1386,14 @@ private const(char)[] real2String(double value,
return buffer;
}
- return errol1(value, buffer, exponent);
+ if (value > 9.007199254740992e15 && value < 3.40282366920938e38)
+ {
+ return errol2(value, buffer, exponent);
+ }
+ else
+ {
+ return errol1(value, buffer, exponent);
+ }
}
private void formatReal(T)(ref T arg, ref String result)