1010module core.internal.convert ;
1111import core.internal.traits : Unqual;
1212
13+ /+
14+ A @nogc function can allocate memory during CTFE.
15+ +/
16+ @nogc nothrow pure @trusted
17+ private ubyte [] ctfe_alloc ()(size_t n)
18+ {
19+ if (! __ctfe)
20+ {
21+ assert (0 , " CTFE only" );
22+ }
23+ else
24+ {
25+ static ubyte [] alloc (size_t x) nothrow pure
26+ {
27+ if (__ctfe) // Needed to prevent _d_newarray from appearing in compiled prorgam.
28+ return new ubyte [x];
29+ else
30+ assert (0 );
31+ }
32+ return (cast (ubyte [] function (size_t ) @nogc nothrow pure ) &alloc)(n);
33+ }
34+ }
35+
1336@trusted pure nothrow
14- const (ubyte )[] toUbyte (T)(ref T val) if (is (Unqual! T == float ) || is (Unqual! T == double ) || is (Unqual! T == real ) ||
37+ const (ubyte )[] toUbyte (T)(const ref T val) if (is (Unqual! T == float ) || is (Unqual! T == double ) || is (Unqual! T == real ) ||
1538 is (Unqual! T == ifloat ) || is (Unqual! T == idouble ) || is (Unqual! T == ireal ))
1639{
1740 static const (ubyte )[] reverse_ (const (ubyte )[] arr)
1841 {
19- ubyte [] buff = new ubyte [ arr.length] ;
42+ ubyte [] buff = ctfe_alloc( arr.length) ;
2043 foreach (k, v; arr)
2144 {
2245 buff[$- k- 1 ] = v;
@@ -31,17 +54,35 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == float) || is(Unqual!T ==
3154 uint exp = parsed.exponent;
3255 uint sign = parsed.sign;
3356
34- ubyte [T.sizeof ] buff;
57+ ubyte [] buff = ctfe_alloc(T.sizeof) ;
3558 size_t off_bytes = 0 ;
3659 size_t off_bits = 0 ;
60+ // Quadruples won't fit in one ulong, so check for that.
61+ enum mantissaMax = FloatTraits! T.MANTISSA < ulong .sizeof* 8 ?
62+ FloatTraits! T.MANTISSA : ulong .sizeof* 8 ;
3763
38- for (; off_bytes < FloatTraits ! T. MANTISSA / 8 ; ++ off_bytes)
64+ for (; off_bytes < mantissaMax / 8 ; ++ off_bytes)
3965 {
4066 buff[off_bytes] = cast (ubyte )mantissa;
4167 mantissa >>= 8 ;
4268 }
43- off_bits = FloatTraits! T.MANTISSA % 8 ;
44- buff[off_bytes] = cast (ubyte )mantissa;
69+
70+ static if (floatFormat! T == FloatFormat.Quadruple)
71+ {
72+ ulong mantissa2 = parsed.mantissa2;
73+ off_bytes-- ; // go back one, since mantissa only stored data in 56
74+ // bits, ie 7 bytes
75+ for (; off_bytes < FloatTraits! T.MANTISSA / 8 ; ++ off_bytes)
76+ {
77+ buff[off_bytes] = cast (ubyte )mantissa2;
78+ mantissa2 >>= 8 ;
79+ }
80+ }
81+ else
82+ {
83+ off_bits = FloatTraits! T.MANTISSA % 8 ;
84+ buff[off_bytes] = cast (ubyte )mantissa;
85+ }
4586
4687 for (size_t i=0 ; i< FloatTraits! T.EXPONENT / 8 ; ++ i)
4788 {
@@ -60,7 +101,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == float) || is(Unqual!T ==
60101
61102 version (LittleEndian )
62103 {
63- return buff. dup ;
104+ return buff;
64105 }
65106 else
66107 {
@@ -83,8 +124,8 @@ private Float parse(bool is_denormalized = false, T)(T x) if (is(Unqual!T == ifl
83124private Float parse (bool is_denormalized = false , T:real )(T x_) if (floatFormat! T != FloatFormat.Real80)
84125{
85126 Unqual! T x = x_;
86- assert (floatFormat! T != FloatFormat.DoubleDouble && floatFormat ! T != FloatFormat.Quadruple ,
87- " doubledouble and quadruple float formats are not supported in CTFE" );
127+ static assert (floatFormat! T != FloatFormat.DoubleDouble,
128+ " doubledouble float format not supported in CTFE" );
88129 if (x is cast (T)0.0 ) return FloatTraits! T.ZERO ;
89130 if (x is cast (T)- 0.0 ) return FloatTraits! T.NZERO ;
90131 if (x is T.nan) return FloatTraits! T.NAN ;
@@ -103,17 +144,38 @@ private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!
103144 if (is_denormalized)
104145 return Float (0 , 0 , sign);
105146 else
106- return Float ( denormalizedMantissa(x), 0 , sign);
147+ return denormalizedMantissa (x, sign);
107148 }
108149
109150 x2 /= binPow2(e);
110151
111152 static if (! is_denormalized)
112153 x2 -= 1.0 ;
113154
114- x2 *= 2UL << (FloatTraits! T.MANTISSA );
115- ulong mant = shiftrRound(cast (ulong )x2);
116- return Float (mant, exp, sign);
155+ static if (floatFormat! T == FloatFormat.Quadruple)
156+ {
157+ // Store the 112-bit mantissa in two ulongs, specifically the lower 56
158+ // bits of each, with the most significant bits in mantissa2. There's
159+ // an edge case exposed by the labeled test below, where only a subnormal
160+ // with the highest bit set being the 57th bit will "overflow" to the
161+ // 57th bit in mantissa2 with the following logic, but that special case
162+ // is handled by an additional check in denormalizedMantissa for
163+ // Quadruples below.
164+
165+ x2 *= 2UL << (FloatTraits! T.MANTISSA - (ulong .sizeof - 1 )* 8 - 1 );
166+ ulong mant2 = cast (ulong ) x2;
167+ x2 -= mant2;
168+
169+ x2 *= 2UL << ((ulong .sizeof - 1 )* 8 - 1 );
170+ ulong mant = cast (ulong ) x2;
171+ return Float (mant, exp, sign, mant2);
172+ }
173+ else
174+ {
175+ x2 *= 2UL << (FloatTraits! T.MANTISSA );
176+ ulong mant = shiftrRound(cast (ulong )x2);
177+ return Float (mant, exp, sign);
178+ }
117179}
118180
119181@safe pure nothrow
@@ -151,7 +213,7 @@ private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatForm
151213 uint exp = cast (uint )(e + EXPONENT_MED );
152214 if (! exp)
153215 {
154- return Float ( denormalizedMantissa(x), 0 , sign);
216+ return denormalizedMantissa (x, sign);
155217 }
156218 int pow = (FloatTraits! T.MANTISSA - 1 - e);
157219 x *= binPow2((pow / EXPONENT_MED )* EXPONENT_MED ); // To avoid overflow in 2.0L ^^ pow
@@ -165,6 +227,7 @@ private struct Float
165227 ulong mantissa;
166228 uint exponent;
167229 uint sign;
230+ ulong mantissa2;
168231}
169232
170233private template FloatTraits (T) if (floatFormat! T == FloatFormat.Float)
@@ -215,14 +278,14 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.DoubleDouble) /
215278 enum NINF = Float(0 , 0x7ff , 1 );
216279}
217280
218- private template FloatTraits (T) if (floatFormat! T == FloatFormat.Quadruple) // Unsupported in CTFE
281+ private template FloatTraits (T) if (floatFormat! T == FloatFormat.Quadruple)
219282{
220283 enum EXPONENT = 15 ;
221284 enum MANTISSA = 112 ;
222285 enum ZERO = Float(0 , 0 , 0 );
223286 enum NZERO = Float(0 , 0 , 1 );
224- enum NAN = Float(- 1 , 0x7fff , 0 );
225- enum NNAN = Float(- 1 , 0x7fff , 1 );
287+ enum NAN = Float(0 , 0x7fff , 0 , 0x80000000000000UL );
288+ enum NNAN = Float(0 , 0x7fff , 1 , 0x80000000000000UL );
226289 enum INF = Float(0 , 0x7fff , 0 );
227290 enum NINF = Float(0 , 0x7fff , 1 );
228291}
@@ -291,21 +354,49 @@ private uint binLog2(T)(T x)
291354}
292355
293356@safe pure nothrow
294- private ulong denormalizedMantissa (T)(T x) if (floatFormat! T == FloatFormat.Real80)
357+ private Float denormalizedMantissa (T)(T x, uint sign ) if (floatFormat! T == FloatFormat.Real80)
295358{
296359 x *= 2.0L ^^ FloatTraits! T.MANTISSA ;
297360 auto fl = parse(x);
298361 uint pow = FloatTraits! T.MANTISSA - fl.exponent + 1 ;
299- return fl.mantissa >> pow;
362+ return Float ( fl.mantissa >> pow, 0 , sign) ;
300363}
301364
302365@safe pure nothrow
303- private ulong denormalizedMantissa (T)(T x) if (floatFormat! T != FloatFormat.Real80)
366+ private Float denormalizedMantissa (T)(T x, uint sign)
367+ if (floatFormat! T == FloatFormat.Float || floatFormat! T == FloatFormat.Double)
304368{
305369 x *= 2.0L ^^ FloatTraits! T.MANTISSA ;
306370 auto fl = parse! true (x);
307371 ulong mant = fl.mantissa >> (FloatTraits! T.MANTISSA - fl.exponent);
308- return shiftrRound (mant);
372+ return Float (shiftrRound(mant), 0 , sign);
373+ }
374+
375+ @safe pure nothrow
376+ private Float denormalizedMantissa (T)(T x, uint sign) if (floatFormat! T == FloatFormat.Quadruple)
377+ {
378+ x *= 2.0L ^^ FloatTraits! T.MANTISSA ;
379+ auto fl = parse! true (x);
380+ uint offset = FloatTraits! T.MANTISSA - fl.exponent + 1 ;
381+ enum mantissaSize = (ulong .sizeof - 1 ) * 8 ;
382+
383+ if (offset < mantissaSize)
384+ { // Create a new mantissa ulong with the trailing mantissa2 bits that
385+ // need to be shifted into mantissa, by shifting the needed bits left,
386+ // zeroing out the first byte, and then ORing it with mantissa shifted
387+ // right by offset.
388+
389+ ulong shiftedMantissa = ((fl.mantissa2 << (mantissaSize - offset)) &
390+ 0x00FFFFFFFFFFFFFFUL ) | fl.mantissa >> offset;
391+ return Float (shiftedMantissa, 0 , sign, fl.mantissa2 >> offset);
392+ }
393+ else if (offset > mantissaSize)
394+ return Float (fl.mantissa2 >> offset - mantissaSize , 0 , sign, 0 );
395+ else
396+ // Handle special case mentioned in parse() above by zeroing out the
397+ // 57'th bit of mantissa2, "shifting" it into mantissa, and setting the
398+ // first bit of mantissa2.
399+ return Float (fl.mantissa2 & 0x00FFFFFFFFFFFFFFUL , 0 , sign, 1 );
309400}
310401
311402version (unittest )
@@ -403,6 +494,8 @@ version (unittest)
403494
404495 testNumberConvert! (" real.min_normal/2" );
405496 testNumberConvert! (" real.min_normal/2UL^^63" );
497+ // check subnormal storage edge case for Quadruple
498+ testNumberConvert! (" real.min_normal/2UL^^56" );
406499 // testNumberConvert!("real.min_normal/19"); // XGDC: ct[0] == 0, rt[0] == 27
407500 // testNumberConvert!("real.min_normal/17"); // XGDC: ct[0= == 128, rt[0] == 136
408501
0 commit comments