source: cpp/PrintFloat/PrintFloat.cpp @ 823

Last change on this file since 823 was 823, checked in by Maciej Komosinski, 5 years ago

Added "PrintFloat?" implementation of the Dragon4 algorithm, since C++ still lacks built-in "intelligent" printing of floating point values with full precision

File size: 24.3 KB
RevLine 
[823]1// One modification by macko in FormatScientific(): use 3 digits of exponent only if necessary (e+123), otherwise use two if necessary (e+45), otherwise use one (e+6)
2// This is consistent with java and javascript, and partially with python (which never uses one digit, only two or three).
3
4
5/******************************************************************************
6  Copyright (c) 2014 Ryan Juckett
7  http://www.ryanjuckett.com/
8 
9  This software is provided 'as-is', without any express or implied
10  warranty. In no event will the authors be held liable for any damages
11  arising from the use of this software.
12 
13  Permission is granted to anyone to use this software for any purpose,
14  including commercial applications, and to alter it and redistribute it
15  freely, subject to the following restrictions:
16 
17  1. The origin of this software must not be misrepresented; you must not
18     claim that you wrote the original software. If you use this software
19     in a product, an acknowledgment in the product documentation would be
20     appreciated but is not required.
21 
22  2. Altered source versions must be plainly marked as such, and must not be
23     misrepresented as being the original software.
24 
25  3. This notice may not be removed or altered from any source
26     distribution.
27******************************************************************************/
28
29#include "PrintFloat.h"
30#include "Dragon4.h"
31#include "Math.h"
32
33#include <string.h>
34
35//******************************************************************************
36// Helper union to decompose a 32-bit IEEE float.
37// sign:      1 bit
38// exponent:  8 bits
39// mantissa: 23 bits
40//******************************************************************************
41union tFloatUnion32
42{
43    tB   IsNegative() const  { return (m_integer >> 31) != 0; }
44    tU32 GetExponent() const { return (m_integer >> 23) & 0xFF; }
45    tU32 GetMantissa() const { return m_integer & 0x7FFFFF; }
46 
47    tF32 m_floatingPoint;
48    tU32 m_integer;
49};
50 
51//******************************************************************************
52// Helper union to decompose a 64-bit IEEE float.
53// sign:      1 bit
54// exponent: 11 bits
55// mantissa: 52 bits
56//******************************************************************************
57union tFloatUnion64
58{
59    tB   IsNegative() const  { return (m_integer >> 63) != 0; }
60    tU32 GetExponent() const { return (m_integer >> 52) & 0x7FF; }
61    tU64 GetMantissa() const { return m_integer & 0xFFFFFFFFFFFFFull; }
62 
63    tF64 m_floatingPoint;
64    tU64 m_integer;
65};
66
67//******************************************************************************
68// Outputs the positive number with positional notation: ddddd.dddd
69// The output is always NUL terminated and the output length (not including the
70// NUL) is returned.
71//******************************************************************************
72tU32 FormatPositional
73(
74    tC8 *   pOutBuffer,         // buffer to output into
75    tU32    bufferSize,         // maximum characters that can be printed to pOutBuffer
76    tU64    mantissa,           // value significand
77    tS32    exponent,           // value exponent in base 2
78    tU32    mantissaHighBitIdx, // index of the highest set mantissa bit
79    tB      hasUnequalMargins,  // is the high margin twice as large as the low margin
80    tS32    precision           // Negative prints as many digits as are needed for a unique
81                                //  number. Positive specifies the maximum number of
82                                //  significant digits to print past the decimal point.
83)
84{
85    RJ_ASSERT(bufferSize > 0);
86 
87    tS32 printExponent;
88    tU32 numPrintDigits;
89         
90    tU32 maxPrintLen = bufferSize - 1;
91 
92    if (precision < 0)
93    {
94        numPrintDigits = Dragon4(   mantissa,
95                                    exponent,
96                                    mantissaHighBitIdx,
97                                    hasUnequalMargins,
98                                    CutoffMode_Unique,
99                                    0,
100                                    pOutBuffer,
101                                    maxPrintLen,
102                                    &printExponent );
103    }
104    else
105    {
106        numPrintDigits = Dragon4(   mantissa,
107                                    exponent,
108                                    mantissaHighBitIdx,
109                                    hasUnequalMargins,
110                                    CutoffMode_FractionLength,
111                                    precision,
112                                    pOutBuffer,
113                                    maxPrintLen,
114                                    &printExponent );
115    }
116 
117    RJ_ASSERT( numPrintDigits > 0 );
118    RJ_ASSERT( numPrintDigits <= bufferSize );
119 
120    // track the number of digits past the decimal point that have been printed
121    tU32 numFractionDigits = 0;
122                 
123    // if output has a whole number
124    if (printExponent >= 0)
125    {
126        // leave the whole number at the start of the buffer
127        tU32 numWholeDigits = printExponent+1;
128        if (numPrintDigits < numWholeDigits)
129        {
130            // don't overflow the buffer
131            if (numWholeDigits > maxPrintLen)
132                numWholeDigits = maxPrintLen;
133 
134            // add trailing zeros up to the decimal point
135            for ( ; numPrintDigits < numWholeDigits; ++numPrintDigits )
136                pOutBuffer[numPrintDigits] = '0';
137        }
138        // insert the decimal point prior to the fraction
139        else if (numPrintDigits > (tU32)numWholeDigits)
140        {
141            numFractionDigits = numPrintDigits - numWholeDigits;
142            tU32 maxFractionDigits = maxPrintLen - numWholeDigits - 1;
143            if (numFractionDigits > maxFractionDigits)
144                numFractionDigits = maxFractionDigits;
145 
146            memmove(pOutBuffer + numWholeDigits + 1, pOutBuffer + numWholeDigits, numFractionDigits);
147            pOutBuffer[numWholeDigits] = '.';
148            numPrintDigits = numWholeDigits + 1 + numFractionDigits;
149        }
150    }
151    else
152    {
153        // shift out the fraction to make room for the leading zeros
154        if (maxPrintLen > 2)
155        {
156            tU32 numFractionZeros = (tU32)-printExponent - 1;
157            tU32 maxFractionZeros = maxPrintLen - 2;
158            if (numFractionZeros > maxFractionZeros)
159                numFractionZeros = maxFractionZeros;
160 
161            tU32 digitsStartIdx = 2 + numFractionZeros;
162                     
163            // shift the significant digits right such that there is room for leading zeros
164            numFractionDigits = numPrintDigits;
165            tU32 maxFractionDigits = maxPrintLen - digitsStartIdx;
166            if (numFractionDigits > maxFractionDigits)
167                numFractionDigits = maxFractionDigits;
168 
169            memmove(pOutBuffer + digitsStartIdx, pOutBuffer, numFractionDigits);
170 
171            // insert the leading zeros
172            for (tU32 i = 2; i < digitsStartIdx; ++i)
173                pOutBuffer[i] = '0';
174 
175            // update the counts
176            numFractionDigits += numFractionZeros;
177            numPrintDigits = numFractionDigits;
178        }
179 
180        // add the decimal point
181        if (maxPrintLen > 1)
182        {
183            pOutBuffer[1] = '.';
184            numPrintDigits += 1;
185        }
186 
187        // add the initial zero
188        if (maxPrintLen > 0)
189        {
190            pOutBuffer[0] = '0';
191            numPrintDigits += 1;
192        }
193    }
194             
195    // add trailing zeros up to precision length
196    if (precision > (tS32)numFractionDigits && numPrintDigits < maxPrintLen)
197    {
198        // add a decimal point if this is the first fractional digit we are printing
199        if (numFractionDigits == 0)
200        {
201            pOutBuffer[numPrintDigits++] = '.';
202        }
203 
204        // compute the number of trailing zeros needed
205        tU32 totalDigits = numPrintDigits + (precision - numFractionDigits);
206        if (totalDigits > maxPrintLen)
207            totalDigits = maxPrintLen;
208 
209        for ( ; numPrintDigits < totalDigits; ++numPrintDigits )
210            pOutBuffer[numPrintDigits] = '0';
211    }
212 
213    // terminate the buffer
214    RJ_ASSERT( numPrintDigits <= maxPrintLen );
215    pOutBuffer[numPrintDigits] = '\0';
216 
217    return numPrintDigits;
218}
219
220//******************************************************************************
221// Outputs the positive number with scientific notation: d.dddde[sign]ddd
222// The output is always NUL terminated and the output length (not including the
223// NUL) is returned.
224//******************************************************************************
225tU32 FormatScientific
226(
227    tC8 *   pOutBuffer,         // buffer to output into
228    tU32    bufferSize,         // maximum characters that can be printed to pOutBuffer
229    tU64    mantissa,           // value significand
230    tS32    exponent,           // value exponent in base 2
231    tU32    mantissaHighBitIdx, // index of the highest set mantissa bit
232    tB      hasUnequalMargins,  // is the high margin twice as large as the low margin
233    tS32    precision           // Negative prints as many digits as are needed for a unique
234                                //  number. Positive specifies the maximum number of
235                                //  significant digits to print past the decimal point.
236)
237{
238    RJ_ASSERT(bufferSize > 0);
239 
240    tS32 printExponent;
241    tU32 numPrintDigits;
242         
243    if (precision < 0)
244    {
245        numPrintDigits = Dragon4(   mantissa,
246                                    exponent,
247                                    mantissaHighBitIdx,
248                                    hasUnequalMargins,
249                                    CutoffMode_Unique,
250                                    0,
251                                    pOutBuffer,
252                                    bufferSize,
253                                    &printExponent );
254    }
255    else
256    {
257        numPrintDigits = Dragon4(   mantissa,
258                                    exponent,
259                                    mantissaHighBitIdx,
260                                    hasUnequalMargins,
261                                    CutoffMode_TotalLength,
262                                    precision + 1,
263                                    pOutBuffer,
264                                    bufferSize,
265                                    &printExponent );
266    }
267 
268    RJ_ASSERT( numPrintDigits > 0 );
269    RJ_ASSERT( numPrintDigits <= bufferSize );
270 
271    tC8 * pCurOut = pOutBuffer;
272 
273    // keep the whole number as the first digit
274    if (bufferSize > 1)
275    {
276        pCurOut += 1;
277        bufferSize -= 1;
278    }
279 
280    // insert the decimal point prior to the fractional number
281    tU32 numFractionDigits = numPrintDigits-1;
282    if (numFractionDigits > 0 && bufferSize > 1)
283    {
284        tU32 maxFractionDigits = bufferSize-2;
285        if (numFractionDigits > maxFractionDigits)
286            numFractionDigits =  maxFractionDigits;
287 
288        memmove(pCurOut + 1, pCurOut, numFractionDigits);
289        pCurOut[0] = '.';
290        pCurOut += (1 + numFractionDigits);
291        bufferSize -= (1 + numFractionDigits);
292    }
293 
294    // add trailing zeros up to precision length
295    if (precision > (tS32)numFractionDigits && bufferSize > 1)
296    {
297        // add a decimal point if this is the first fractional digit we are printing
298        if (numFractionDigits == 0)
299        {
300            *pCurOut = '.';
301            ++pCurOut;
302            --bufferSize;
303        }
304 
305        // compute the number of trailing zeros needed
306        tU32 numZeros = (precision - numFractionDigits);
307        if (numZeros > bufferSize-1)
308            numZeros = bufferSize-1;
309 
310        for (tC8 * pEnd = pCurOut + numZeros; pCurOut < pEnd; ++pCurOut )
311            *pCurOut = '0';
312    }
313 
314    // print the exponent into a local buffer and copy into output buffer
315    if (bufferSize > 1)
316    {
317        tC8 exponentBuffer[5]; //we will need 3, 4 or 5 chars
318        exponentBuffer[0] = 'e';
319        if (printExponent >= 0)
320        {
321            exponentBuffer[1] = '+';
322        }
323        else
324        {
325            exponentBuffer[1] = '-';
326            printExponent = -printExponent;
327        }
328 
329        RJ_ASSERT(printExponent < 1000);
330        tU32 hundredsPlace  = printExponent / 100;
331        tU32 tensPlace      = (printExponent - hundredsPlace*100) / 10;
332        tU32 onesPlace      = (printExponent - hundredsPlace*100 - tensPlace*10);
333
334                // modified by macko: use 3 digits of exponent only if necessary (e+123), otherwise use two if necessary (e+45), otherwise use one (e+6)
335                unsigned int bufferIndex = 2;
336                if (hundredsPlace != 0) //3 digits needed
337                        exponentBuffer[bufferIndex++] = (tC8)('0' + hundredsPlace);
338                if (hundredsPlace != 0 || tensPlace != 0) //2 digits needed
339                        exponentBuffer[bufferIndex++] = (tC8)('0' + tensPlace);
340                exponentBuffer[bufferIndex++] = (tC8)('0' + onesPlace);
341                // now bufferIndex indicates how many characters of exponentBuffer were used
342
343                // copy the exponent buffer into the output
344                tU32 maxExponentSize = bufferSize - 1;
345                tU32 exponentSize = (bufferIndex < maxExponentSize) ? bufferIndex : maxExponentSize;
346                memcpy( pCurOut, exponentBuffer, exponentSize );
347 
348        pCurOut += exponentSize;
349        bufferSize -= exponentSize;
350    }
351 
352    RJ_ASSERT( bufferSize > 0 );
353    pCurOut[0] = '\0';
354 
355    return pCurOut - pOutBuffer;
356}
357
358//******************************************************************************
359// Print a hexadecimal value with a given width.
360// The output string is always NUL terminated and the string length (not
361// including the NUL) is returned.
362//******************************************************************************
363static tU32 PrintHex(tC8 * pOutBuffer, tU32 bufferSize, tU64 value, tU32 width)
364{
365        const tC8 digits[] = "0123456789abcdef";
366
367        RJ_ASSERT(bufferSize > 0);
368
369        tU32 maxPrintLen = bufferSize-1;
370        if (width > maxPrintLen)
371                width = maxPrintLen;
372
373        tC8 * pCurOut = pOutBuffer;
374        while (width > 0)
375        {
376                --width;
377                       
378                tU8 digit = (tU8)((value >> 4ull*(tU64)width) & 0xF);
379                *pCurOut = digits[digit];
380
381                ++pCurOut;
382        }
383
384        *pCurOut = '\0';
385        return pCurOut - pOutBuffer;
386}
387 
388//******************************************************************************
389// Print special case values for infinities and NaNs.
390// The output string is always NUL terminated and the string length (not
391// including the NUL) is returned.
392//******************************************************************************
393static tU32 PrintInfNan(tC8 * pOutBuffer, tU32 bufferSize, tU64 mantissa, tU32 mantissaHexWidth)
394{
395    RJ_ASSERT(bufferSize > 0);
396 
397    tU32 maxPrintLen = bufferSize-1;
398 
399    // Check for infinity
400    if (mantissa == 0)
401    {
402        // copy and make sure the buffer is terminated
403        tU32 printLen = (3 < maxPrintLen) ? 3 : maxPrintLen;
404        ::memcpy( pOutBuffer, "Inf", printLen );
405        pOutBuffer[printLen] = '\0';
406        return printLen;
407    }
408    else
409    {
410        // copy and make sure the buffer is terminated
411        tU32 printLen = (3 < maxPrintLen) ? 3 : maxPrintLen;
412        ::memcpy( pOutBuffer, "NaN", printLen );
413        pOutBuffer[printLen] = '\0';
414                 
415        // append HEX value
416        if (maxPrintLen > 3)
417            printLen += PrintHex(pOutBuffer+3, bufferSize-3, mantissa, mantissaHexWidth);
418                                 
419        return printLen;
420    }
421}
422
423//******************************************************************************
424// Print a 32-bit floating-point number as a decimal string.
425// The output string is always NUL terminated and the string length (not
426// including the NUL) is returned.
427//******************************************************************************
428tU32 PrintFloat32
429(
430    tC8 *               pOutBuffer,     // buffer to output into
431    tU32                bufferSize,     // size of pOutBuffer
432    tF32                value,          // value to print
433    tPrintFloatFormat   format,         // format to print with
434    tS32                precision       // If negative, the minimum number of digits to represent a
435                                        // unique 32-bit floating point value is output. Otherwise,
436                                        // this is the number of digits to print past the decimal point.
437)
438{
439    if (bufferSize == 0)
440        return 0;
441 
442    if (bufferSize == 1)
443    {
444        pOutBuffer[0] = '\0';
445        return 0;
446    }
447 
448    // deconstruct the floating point value
449    tFloatUnion32 floatUnion;
450    floatUnion.m_floatingPoint = value;
451    tU32 floatExponent = floatUnion.GetExponent();
452    tU32 floatMantissa = floatUnion.GetMantissa();
453        tU32 prefixLength = 0;
454         
455    // output the sign
456    if (floatUnion.IsNegative())
457    {
458        pOutBuffer[0] = '-';
459        ++pOutBuffer;
460        --bufferSize;
461                ++prefixLength;
462        RJ_ASSERT(bufferSize > 0);
463    }
464 
465    // if this is a special value
466    if (floatExponent == 0xFF)
467    {
468        return PrintInfNan(pOutBuffer, bufferSize, floatMantissa, 6) + prefixLength;
469    }
470    // else this is a number
471    else
472    {               
473        // factor the value into its parts
474        tU32 mantissa;
475        tS32 exponent; 
476        tU32 mantissaHighBitIdx;
477        tB hasUnequalMargins;
478        if (floatExponent != 0)
479        {
480            // normalized
481            // The floating point equation is:
482            //  value = (1 + mantissa/2^23) * 2 ^ (exponent-127)
483            // We convert the integer equation by factoring a 2^23 out of the exponent
484            //  value = (1 + mantissa/2^23) * 2^23 * 2 ^ (exponent-127-23)
485            //  value = (2^23 + mantissa) * 2 ^ (exponent-127-23)
486            // Because of the implied 1 in front of the mantissa we have 24 bits of precision.
487            //   m = (2^23 + mantissa)
488            //   e = (exponent-127-23)
489            mantissa            = (1UL << 23) | floatMantissa;
490            exponent            = floatExponent - 127 - 23;
491            mantissaHighBitIdx  = 23;
492            hasUnequalMargins   = (floatExponent != 1) && (floatMantissa == 0);
493        }
494        else
495        {
496            // denormalized
497            // The floating point equation is:
498            //  value = (mantissa/2^23) * 2 ^ (1-127)
499            // We convert the integer equation by factoring a 2^23 out of the exponent
500            //  value = (mantissa/2^23) * 2^23 * 2 ^ (1-127-23)
501            //  value = mantissa * 2 ^ (1-127-23)
502            // We have up to 23 bits of precision.
503            //   m = (mantissa)
504            //   e = (1-127-23)
505            mantissa           = floatMantissa;
506            exponent           = 1 - 127 - 23;
507            mantissaHighBitIdx = LogBase2(mantissa);
508            hasUnequalMargins   = false;
509        }
510 
511        // format the value
512        switch (format)
513        {
514        case PrintFloatFormat_Positional:
515            return FormatPositional(    pOutBuffer,
516                                        bufferSize,
517                                        mantissa,
518                                        exponent,
519                                        mantissaHighBitIdx,
520                                        hasUnequalMargins,
521                                        precision ) + prefixLength;
522 
523        case PrintFloatFormat_Scientific:
524            return FormatScientific(    pOutBuffer,
525                                        bufferSize,
526                                        mantissa,
527                                        exponent,
528                                        mantissaHighBitIdx,
529                                        hasUnequalMargins,
530                                        precision ) + prefixLength;
531 
532        default:
533            pOutBuffer[0] = '\0';
534            return 0;
535        }
536    }
537}
538 
539//******************************************************************************
540// Print a 64-bit floating-point number as a decimal string.
541// The output string is always NUL terminated and the string length (not
542// including the NUL) is returned.
543//******************************************************************************
544tU32 PrintFloat64
545(
546    tC8 *               pOutBuffer,     // buffer to output into
547    tU32                bufferSize,     // size of pOutBuffer
548    tF64                value,          // value to print
549    tPrintFloatFormat   format,         // format to print with
550    tS32                precision       // If negative, the minimum number of digits to represent a
551                                        // unique 64-bit floating point value is output. Otherwise,
552                                        // this is the number of digits to print past the decimal point.
553)
554{
555    if (bufferSize == 0)
556        return 0;
557 
558    if (bufferSize == 1)
559    {
560        pOutBuffer[0] = '\0';
561        return 0;
562    }
563 
564    // deconstruct the floating point value
565    tFloatUnion64 floatUnion;
566    floatUnion.m_floatingPoint = value;
567    tU32 floatExponent = floatUnion.GetExponent();
568    tU64 floatMantissa = floatUnion.GetMantissa();
569        tU32 prefixLength = 0;
570         
571    // output the sign
572    if (floatUnion.IsNegative())
573    {
574        pOutBuffer[0] = '-';
575        ++pOutBuffer;
576        --bufferSize;
577                ++prefixLength;
578        RJ_ASSERT(bufferSize > 0);
579    }
580 
581    // if this is a special value
582    if (floatExponent == 0x7FF)
583    {
584        return PrintInfNan(pOutBuffer, bufferSize, floatMantissa, 13) + prefixLength;
585    }
586    // else this is a number
587    else
588    {
589        // factor the value into its parts
590        tU64 mantissa;
591        tS32 exponent; 
592        tU32 mantissaHighBitIdx;
593        tB hasUnequalMargins;
594         
595        if (floatExponent != 0)
596        {       
597            // normal
598            // The floating point equation is:
599            //  value = (1 + mantissa/2^52) * 2 ^ (exponent-1023)
600            // We convert the integer equation by factoring a 2^52 out of the exponent
601            //  value = (1 + mantissa/2^52) * 2^52 * 2 ^ (exponent-1023-52)
602            //  value = (2^52 + mantissa) * 2 ^ (exponent-1023-52)
603            // Because of the implied 1 in front of the mantissa we have 53 bits of precision.
604            //   m = (2^52 + mantissa)
605            //   e = (exponent-1023+1-53)
606            mantissa            = (1ull << 52) | floatMantissa;
607            exponent            = floatExponent - 1023 - 52;
608            mantissaHighBitIdx  = 52;
609            hasUnequalMargins   = (floatExponent != 1) && (floatMantissa == 0);
610        }
611        else
612        {
613            // subnormal
614            // The floating point equation is:
615            //  value = (mantissa/2^52) * 2 ^ (1-1023)
616            // We convert the integer equation by factoring a 2^52 out of the exponent
617            //  value = (mantissa/2^52) * 2^52 * 2 ^ (1-1023-52)
618            //  value = mantissa * 2 ^ (1-1023-52)
619            // We have up to 52 bits of precision.
620            //   m = (mantissa)
621            //   e = (1-1023-52)
622            mantissa            = floatMantissa;
623            exponent            = 1 - 1023 - 52;
624            mantissaHighBitIdx  = LogBase2(mantissa);
625            hasUnequalMargins   = false;
626        }
627 
628        // format the value
629        switch (format)
630        {
631        case PrintFloatFormat_Positional:
632            return FormatPositional(    pOutBuffer,
633                                        bufferSize,
634                                        mantissa,
635                                        exponent,
636                                        mantissaHighBitIdx,
637                                        hasUnequalMargins,
638                                        precision ) + prefixLength;
639 
640        case PrintFloatFormat_Scientific:
641            return FormatScientific(    pOutBuffer,
642                                        bufferSize,
643                                        mantissa,
644                                        exponent,
645                                        mantissaHighBitIdx,
646                                        hasUnequalMargins,
647                                        precision ) + prefixLength;
648 
649        default:
650            pOutBuffer[0] = '\0';
651            return 0;
652        }
653    }
654}
Note: See TracBrowser for help on using the repository browser.