1
0
mirror of https://gitlab.com/rnger/amath synced 2025-10-06 02:49:59 +00:00
Files
amath/doc/man/man3/dragon4.h.3
2017-01-24 22:03:15 +01:00

483 lines
20 KiB
Groff

.TH "lib/dconv/dragon4.h" 3 "Tue Jan 24 2017" "Version 1.6.2" "amath" \" -*- nroff -*-
.ad l
.nh
.SH NAME
lib/dconv/dragon4.h \- Convert floating point format to a decimal number in string format\&.
.SH SYNOPSIS
.br
.PP
\fC#include 'dstandard\&.h'\fP
.br
.SS "Enumerations"
.in +1c
.ti -1c
.RI "enum \fBtCutoffMode\fP { \fBCutoffMode_Unique\fP, \fBCutoffMode_TotalLength\fP, \fBCutoffMode_FractionLength\fP }"
.br
.in -1c
.SS "Functions"
.in +1c
.ti -1c
.RI "\fBtU32\fP \fBDragon4\fP (\fBtU64\fP mantissa, \fBtS32\fP exponent, \fBtU32\fP mantissaHighBitIdx, \fBtB\fP hasUnequalMargins, enum \fBtCutoffMode\fP cutoffMode, \fBtU32\fP cutoffNumber, \fBtC8\fP *pOutBuffer, \fBtU32\fP bufferSize, \fBtS32\fP *pOutExponent)"
.br
.RI "\fIDragon4 main\&. \fP"
.in -1c
.SH "Detailed Description"
.PP
Convert floating point format to a decimal number in string format\&.
This is an implementation the Dragon4 algorithm to convert a binary number in floating point format to a decimal number in string format\&. The function returns the number of digits written to the output buffer and the output is not NUL terminated\&.
.PP
Downloaded from:
.br
http://www.ryanjuckett.com/
.PP
Definition in file \fBdragon4\&.h\fP\&.
.SH "Enumeration Type Documentation"
.PP
.SS "enum \fBtCutoffMode\fP"
.PP
\fBEnumerator\fP
.in +1c
.TP
\fB\fICutoffMode_Unique \fP\fP
.TP
\fB\fICutoffMode_TotalLength \fP\fP
.TP
\fB\fICutoffMode_FractionLength \fP\fP
.PP
Definition at line 75 of file dragon4\&.h\&.
.PP
.nf
76 {
77 CutoffMode_Unique, // as many digits as necessary to print a uniquely identifiable number
78 CutoffMode_TotalLength, // up to cutoffNumber significant digits
79 CutoffMode_FractionLength, // up to cutoffNumber significant digits past the decimal point
80 };
.fi
.SH "Function Documentation"
.PP
.SS "\fBtU32\fP Dragon4 (\fBtU64\fP mantissa, \fBtS32\fP exponent, \fBtU32\fP mantissaHighBitIdx, \fBtB\fP hasUnequalMargins, enum \fBtCutoffMode\fP cutoffMode, \fBtU32\fP cutoffNumber, \fBtC8\fP * pOutBuffer, \fBtU32\fP bufferSize, \fBtS32\fP * pOutExponent)"
.PP
Dragon4 main\&. Downloaded from:
.br
http://www.ryanjuckett.com/
.PP
This is an implementation the Dragon4 algorithm to convert a binary number in floating point format to a decimal number in string format\&. The function returns the number of digits written to the output buffer and the output is not NUL terminated\&.
.PP
The floating point input value is (mantissa * 2^exponent)\&.
.PP
See the following papers for more information on the algorithm:
.br
'How to Print Floating-Point Numbers Accurately'
.br
Steele and White
.br
http://kurtstephens.com/files/p372-steele.pdf
.br
'Printing Floating-Point Numbers Quickly and Accurately'
.br
Burger and Dybvig
.br
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf
.br
.PP
Definition at line 770 of file dragon4\&.cpp\&.
.PP
References BigInt_Add(), BigInt_Compare(), BigInt_DivideWithRemainder_MaxQuotient9(), BigInt_Multiply(), BigInt_Multiply10(), BigInt_Multiply2(), BigInt_MultiplyPow10(), BigInt_Pow10(), BigInt_Pow2(), BigInt_ShiftLeft(), CutoffMode_FractionLength, CutoffMode_TotalLength, CutoffMode_Unique, tBigInt::GetBlock(), tBigInt::GetLength(), tBigInt::IsZero(), LogBase2(), tBigInt::operator=(), tBigInt::SetU32(), and tBigInt::SetU64()\&.
.PP
Referenced by FormatPositional(), and FormatScientific()\&.
.PP
.nf
781 {
782 tC8 * pCurDigit = pOutBuffer;
783
784 RJ_ASSERT( bufferSize > 0 );
785
786 // if the mantissa is zero, the value is zero regardless of the exponent
787 if (mantissa == 0)
788 {
789 *pCurDigit = '0';
790 *pOutExponent = 0;
791 return 1;
792 }
793
794 // compute the initial state in integral form such that
795 // value = scaledValue / scale
796 // marginLow = scaledMarginLow / scale
797 tBigInt scale; // positive scale applied to value and margin such that they can be
798 // represented as whole numbers
799 tBigInt scaledValue; // scale * mantissa
800 tBigInt scaledMarginLow; // scale * 0\&.5 * (distance between this floating-point number and its
801 // immediate lower value)
802
803 // For normalized IEEE floating point values, each time the exponent is incremented the margin also
804 // doubles\&. That creates a subset of transition numbers where the high margin is twice the size of
805 // the low margin\&.
806 tBigInt * pScaledMarginHigh;
807 tBigInt optionalMarginHigh;
808
809 if ( hasUnequalMargins )
810 {
811 // if we have no fractional component
812 if (exponent > 0)
813 {
814 // 1) Expand the input value by multiplying out the mantissa and exponent\&. This represents
815 // the input value in its whole number representation\&.
816 // 2) Apply an additional scale of 2 such that later comparisons against the margin values
817 // are simplified\&.
818 // 3) Set the margin value to the lowest mantissa bit's scale\&.
819
820 // scaledValue = 2 * 2 * mantissa*2^exponent
821 scaledValue\&.SetU64( 4 * mantissa );
822 BigInt_ShiftLeft( &scaledValue, exponent );
823
824 // scale = 2 * 2 * 1
825 scale\&.SetU32( 4 );
826
827 // scaledMarginLow = 2 * 2^(exponent-1)
828 BigInt_Pow2( &scaledMarginLow, exponent );
829
830 // scaledMarginHigh = 2 * 2 * 2^(exponent-1)
831 BigInt_Pow2( &optionalMarginHigh, exponent + 1 );
832 }
833 // else we have a fractional exponent
834 else
835 {
836 // In order to track the mantissa data as an integer, we store it as is with a large scale
837
838 // scaledValue = 2 * 2 * mantissa
839 scaledValue\&.SetU64( 4 * mantissa );
840
841 // scale = 2 * 2 * 2^(-exponent)
842 BigInt_Pow2(&scale, -exponent + 2 );
843
844 // scaledMarginLow = 2 * 2^(-1)
845 scaledMarginLow\&.SetU32( 1 );
846
847 // scaledMarginHigh = 2 * 2 * 2^(-1)
848 optionalMarginHigh\&.SetU32( 2 );
849 }
850
851 // the high and low margins are different
852 pScaledMarginHigh = &optionalMarginHigh;
853 }
854 else
855 {
856 // if we have no fractional component
857 if (exponent > 0)
858 {
859 // 1) Expand the input value by multiplying out the mantissa and exponent\&. This represents
860 // the input value in its whole number representation\&.
861 // 2) Apply an additional scale of 2 such that later comparisons against the margin values
862 // are simplified\&.
863 // 3) Set the margin value to the lowest mantissa bit's scale\&.
864
865 // scaledValue = 2 * mantissa*2^exponent
866 scaledValue\&.SetU64( 2 * mantissa );
867 BigInt_ShiftLeft( &scaledValue, exponent );
868
869 // scale = 2 * 1
870 scale\&.SetU32( 2 );
871
872 // scaledMarginLow = 2 * 2^(exponent-1)
873 BigInt_Pow2( &scaledMarginLow, exponent );
874 }
875 // else we have a fractional exponent
876 else
877 {
878 // In order to track the mantissa data as an integer, we store it as is with a large scale
879
880 // scaledValue = 2 * mantissa
881 scaledValue\&.SetU64( 2 * mantissa );
882
883 // scale = 2 * 2^(-exponent)
884 BigInt_Pow2(&scale, -exponent + 1 );
885
886 // scaledMarginLow = 2 * 2^(-1)
887 scaledMarginLow\&.SetU32( 1 );
888 }
889
890 // the high and low margins are equal
891 pScaledMarginHigh = &scaledMarginLow;
892 }
893
894 // Compute an estimate for digitExponent that will be correct or undershoot by one\&.
895 // This optimization is based on the paper "Printing Floating-Point Numbers Quickly and Accurately"
896 // by Burger and Dybvig http://citeseerx\&.ist\&.psu\&.edu/viewdoc/download?doi=10\&.1\&.1\&.72\&.4656&rep=rep1&type=pdf
897 // We perform an additional subtraction of 0\&.69 to increase the frequency of a failed estimate
898 // because that lets us take a faster branch in the code\&. 0\&.69 is chosen because 0\&.69 + log10(2) is
899 // less than one by a reasonable epsilon that will account for any floating point error\&.
900 //
901 // We want to set digitExponent to floor(log10(v)) + 1
902 // v = mantissa*2^exponent
903 // log2(v) = log2(mantissa) + exponent;
904 // log10(v) = log2(v) * log10(2)
905 // floor(log2(v)) = mantissaHighBitIdx + exponent;
906 // log10(v) - log10(2) < (mantissaHighBitIdx + exponent) * log10(2) <= log10(v)
907 // log10(v) < (mantissaHighBitIdx + exponent) * log10(2) + log10(2) <= log10(v) + log10(2)
908 // floor( log10(v) ) < ceil( (mantissaHighBitIdx + exponent) * log10(2) ) <= floor( log10(v) ) + 1
909 const tF64 log10_2 = 0\&.30102999566398119521373889472449;
910 tS32 digitExponent = (tS32)(ceil(tF64((tS32)mantissaHighBitIdx + exponent) * log10_2 - 0\&.69));
911
912 // if the digit exponent is smaller than the smallest desired digit for fractional cutoff,
913 // pull the digit back into legal range at which point we will round to the appropriate value\&.
914 // Note that while our value for digitExponent is still an estimate, this is safe because it
915 // only increases the number\&. This will either correct digitExponent to an accurate value or it
916 // will clamp it above the accurate value\&.
917 if (cutoffMode == CutoffMode_FractionLength && digitExponent <= -(tS32)cutoffNumber)
918 {
919 digitExponent = -(tS32)cutoffNumber + 1;
920 }
921
922 // Divide value by 10^digitExponent\&.
923 if (digitExponent > 0)
924 {
925 // The exponent is positive creating a division so we multiply up the scale\&.
926 tBigInt temp;
927 BigInt_MultiplyPow10( &temp, scale, digitExponent );
928 scale = temp;
929 }
930 else if (digitExponent < 0)
931 {
932 // The exponent is negative creating a multiplication so we multiply up the scaledValue,
933 // scaledMarginLow and scaledMarginHigh\&.
934 tBigInt pow10;
935 BigInt_Pow10( &pow10, -digitExponent);
936
937 tBigInt temp;
938 BigInt_Multiply( &temp, scaledValue, pow10);
939 scaledValue = temp;
940
941 BigInt_Multiply( &temp, scaledMarginLow, pow10);
942 scaledMarginLow = temp;
943
944 if (pScaledMarginHigh != &scaledMarginLow)
945 BigInt_Multiply2( pScaledMarginHigh, scaledMarginLow );
946 }
947
948 // If (value + marginHigh) >= 1, our estimate for digitExponent was too low
949 tBigInt scaledValueHigh;
950 BigInt_Add( &scaledValueHigh, scaledValue, *pScaledMarginHigh );
951 if( BigInt_Compare(scaledValueHigh,scale) >= 0 )
952 {
953 // The exponent estimate was incorrect\&.
954 // Increment the exponent and don't perform the premultiply needed
955 // for the first loop iteration\&.
956 digitExponent = digitExponent + 1;
957 }
958 else
959 {
960 // The exponent estimate was correct\&.
961 // Multiply larger by the output base to prepare for the first loop iteration\&.
962 BigInt_Multiply10( &scaledValue );
963 BigInt_Multiply10( &scaledMarginLow );
964 if (pScaledMarginHigh != &scaledMarginLow)
965 BigInt_Multiply2( pScaledMarginHigh, scaledMarginLow );
966 }
967
968 // Compute the cutoff exponent (the exponent of the final digit to print)\&.
969 // Default to the maximum size of the output buffer\&.
970 tS32 cutoffExponent = digitExponent - bufferSize;
971 switch(cutoffMode)
972 {
973 // print digits until we pass the accuracy margin limits or buffer size
974 case CutoffMode_Unique:
975 break;
976
977 // print cutoffNumber of digits or until we reach the buffer size
978 case CutoffMode_TotalLength:
979 {
980 tS32 desiredCutoffExponent = digitExponent - (tS32)cutoffNumber;
981 if (desiredCutoffExponent > cutoffExponent)
982 cutoffExponent = desiredCutoffExponent;
983 }
984 break;
985
986 // print cutoffNumber digits past the decimal point or until we reach the buffer size
987 case CutoffMode_FractionLength:
988 {
989 tS32 desiredCutoffExponent = -(tS32)cutoffNumber;
990 if (desiredCutoffExponent > cutoffExponent)
991 cutoffExponent = desiredCutoffExponent;
992 }
993 break;
994 }
995
996 // Output the exponent of the first digit we will print
997 *pOutExponent = digitExponent-1;
998
999 // In preparation for calling BigInt_DivideWithRemainder_MaxQuotient9(),
1000 // we need to scale up our values such that the highest block of the denominator
1001 // is greater than or equal to 8\&. We also need to guarantee that the numerator
1002 // can never have a length greater than the denominator after each loop iteration\&.
1003 // This requires the highest block of the denominator to be less than or equal to
1004 // 429496729 which is the highest number that can be multiplied by 10 without
1005 // overflowing to a new block\&.
1006 RJ_ASSERT( scale\&.GetLength() > 0 );
1007 tU32 hiBlock = scale\&.GetBlock( scale\&.GetLength() - 1 );
1008 if (hiBlock < 8 || hiBlock > 429496729)
1009 {
1010 // Perform a bit shift on all values to get the highest block of the denominator into
1011 // the range [8,429496729]\&. We are more likely to make accurate quotient estimations
1012 // in BigInt_DivideWithRemainder_MaxQuotient9() with higher denominator values so
1013 // we shift the denominator to place the highest bit at index 27 of the highest block\&.
1014 // This is safe because (2^28 - 1) = 268435455 which is less than 429496729\&. This means
1015 // that all values with a highest bit at index 27 are within range\&.
1016 tU32 hiBlockLog2 = LogBase2(hiBlock);
1017 RJ_ASSERT(hiBlockLog2 < 3 || hiBlockLog2 > 27);
1018 tU32 shift = (32 + 27 - hiBlockLog2) % 32;
1019
1020 BigInt_ShiftLeft( &scale, shift );
1021 BigInt_ShiftLeft( &scaledValue, shift);
1022 BigInt_ShiftLeft( &scaledMarginLow, shift);
1023 if (pScaledMarginHigh != &scaledMarginLow)
1024 BigInt_Multiply2( pScaledMarginHigh, scaledMarginLow );
1025 }
1026
1027 // These values are used to inspect why the print loop terminated so we can properly
1028 // round the final digit\&.
1029 tB low; // did the value get within marginLow distance from zero
1030 tB high; // did the value get within marginHigh distance from one
1031 tU32 outputDigit; // current digit being output
1032
1033 if (cutoffMode == CutoffMode_Unique)
1034 {
1035 // For the unique cutoff mode, we will try to print until we have reached a level of
1036 // precision that uniquely distinguishes this value from its neighbors\&. If we run
1037 // out of space in the output buffer, we terminate early\&.
1038 for (;;)
1039 {
1040 digitExponent = digitExponent-1;
1041
1042 // divide out the scale to extract the digit
1043 outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(&scaledValue, scale);
1044 RJ_ASSERT( outputDigit < 10 );
1045
1046 // update the high end of the value
1047 BigInt_Add( &scaledValueHigh, scaledValue, *pScaledMarginHigh );
1048
1049 // stop looping if we are far enough away from our neighboring values
1050 // or if we have reached the cutoff digit
1051 low = BigInt_Compare(scaledValue, scaledMarginLow) < 0;
1052 high = BigInt_Compare(scaledValueHigh, scale) > 0;
1053 if (low | high | (digitExponent == cutoffExponent))
1054 break;
1055
1056 // store the output digit
1057 *pCurDigit = (tC8)('0' + outputDigit);
1058 ++pCurDigit;
1059
1060 // multiply larger by the output base
1061 BigInt_Multiply10( &scaledValue );
1062 BigInt_Multiply10( &scaledMarginLow );
1063 if (pScaledMarginHigh != &scaledMarginLow)
1064 BigInt_Multiply2( pScaledMarginHigh, scaledMarginLow );
1065 }
1066 }
1067 else
1068 {
1069 // For length based cutoff modes, we will try to print until we
1070 // have exhausted all precision (i\&.e\&. all remaining digits are zeros) or
1071 // until we reach the desired cutoff digit\&.
1072 low = false;
1073 high = false;
1074
1075 for (;;)
1076 {
1077 digitExponent = digitExponent-1;
1078
1079 // divide out the scale to extract the digit
1080 outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(&scaledValue, scale);
1081 RJ_ASSERT( outputDigit < 10 );
1082
1083 if ( scaledValue\&.IsZero() | (digitExponent == cutoffExponent) )
1084 break;
1085
1086 // store the output digit
1087 *pCurDigit = (tC8)('0' + outputDigit);
1088 ++pCurDigit;
1089
1090 // multiply larger by the output base
1091 BigInt_Multiply10(&scaledValue);
1092 }
1093 }
1094
1095 // round off the final digit
1096 // default to rounding down if value got too close to 0
1097 tB roundDown = low;
1098
1099 // if it is legal to round up and down
1100 if (low == high)
1101 {
1102 // round to the closest digit by comparing value with 0\&.5\&. To do this we need to convert
1103 // the inequality to large integer values\&.
1104 // compare( value, 0\&.5 )
1105 // compare( scale * value, scale * 0\&.5 )
1106 // compare( 2 * scale * value, scale )
1107 BigInt_Multiply2(&scaledValue);
1108 tS32 compare = BigInt_Compare(scaledValue, scale);
1109 roundDown = compare < 0;
1110
1111 // if we are directly in the middle, round towards the even digit (i\&.e\&. IEEE rouding rules)
1112 if (compare == 0)
1113 roundDown = (outputDigit & 1) == 0;
1114 }
1115
1116 // print the rounded digit
1117 if (roundDown)
1118 {
1119 *pCurDigit = (tC8)('0' + outputDigit);
1120 ++pCurDigit;
1121 }
1122 else
1123 {
1124 // handle rounding up
1125 if (outputDigit == 9)
1126 {
1127 // find the first non-nine prior digit
1128 for (;;)
1129 {
1130 // if we are at the first digit
1131 if (pCurDigit == pOutBuffer)
1132 {
1133 // output 1 at the next highest exponent
1134 *pCurDigit = '1';
1135 ++pCurDigit;
1136 *pOutExponent += 1;
1137 break;
1138 }
1139
1140 --pCurDigit;
1141 if (*pCurDigit != '9')
1142 {
1143 // increment the digit
1144 *pCurDigit += 1;
1145 ++pCurDigit;
1146 break;
1147 }
1148 }
1149 }
1150 else
1151 {
1152 // values in the range [0,8] can perform a simple round up
1153 *pCurDigit = (tC8)('0' + outputDigit + 1);
1154 ++pCurDigit;
1155 }
1156 }
1157
1158 // return the number of digits output
1159 RJ_ASSERT(pCurDigit - pOutBuffer <= (tPtrDiff)bufferSize);
1160 return pCurDigit - pOutBuffer;
1161 }
.fi
.SH "Author"
.PP
Generated automatically by Doxygen for amath from the source code\&.