source: cpp/PrintFloat/PrintFloat.cpp @ 1024

Last change on this file since 1024 was 834, checked in by Maciej Komosinski, 6 years ago

Renamed Math.h to MathDragon4.h because the <math.h> name clash is unfortunate (and caused problems with C++Builder and filename case-insensitive Windows)

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