|
32 | 32 |
|
33 | 33 | #include "c.h"
|
34 | 34 |
|
| 35 | +#include <ctype.h> |
35 | 36 | #include <limits.h>
|
| 37 | +#include <math.h> |
36 | 38 | #ifndef WIN32
|
37 | 39 | #include <sys/ioctl.h>
|
38 | 40 | #endif
|
@@ -906,27 +908,80 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
|
906 | 908 | PrintfTarget *target)
|
907 | 909 | {
|
908 | 910 | int signvalue = 0;
|
| 911 | + int prec; |
909 | 912 | int vallen;
|
910 | 913 | char fmt[32];
|
911 |
| - char convert[512]; |
912 |
| - int padlen = 0; /* amount to pad */ |
| 914 | + char convert[1024]; |
| 915 | + int zeropadlen = 0; /* amount to pad with zeroes */ |
| 916 | + int padlen = 0; /* amount to pad with spaces */ |
| 917 | + |
| 918 | + /* |
| 919 | + * We rely on the regular C library's sprintf to do the basic conversion, |
| 920 | + * then handle padding considerations here. |
| 921 | + * |
| 922 | + * The dynamic range of "double" is about 1E+-308 for IEEE math, and not |
| 923 | + * too wildly more than that with other hardware. In "f" format, sprintf |
| 924 | + * could therefore generate at most 308 characters to the left of the |
| 925 | + * decimal point; while we need to allow the precision to get as high as |
| 926 | + * 308+17 to ensure that we don't truncate significant digits from very |
| 927 | + * small values. To handle both these extremes, we use a buffer of 1024 |
| 928 | + * bytes and limit requested precision to 350 digits; this should prevent |
| 929 | + * buffer overrun even with non-IEEE math. If the original precision |
| 930 | + * request was more than 350, separately pad with zeroes. |
| 931 | + */ |
| 932 | + if (precision < 0) /* cover possible overflow of "accum" */ |
| 933 | + precision = 0; |
| 934 | + prec = Min(precision, 350); |
913 | 935 |
|
914 |
| - /* we rely on regular C library's sprintf to do the basic conversion */ |
915 | 936 | if (pointflag)
|
916 |
| - sprintf(fmt, "%%.%d%c", precision, type); |
| 937 | + { |
| 938 | + sprintf(fmt, "%%.%d%c", prec, type); |
| 939 | + zeropadlen = precision - prec; |
| 940 | + } |
917 | 941 | else
|
918 | 942 | sprintf(fmt, "%%%c", type);
|
919 | 943 |
|
920 |
| - if (adjust_sign((value < 0), forcesign, &signvalue)) |
| 944 | + if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) |
921 | 945 | value = -value;
|
922 | 946 |
|
923 | 947 | vallen = sprintf(convert, fmt, value);
|
924 | 948 |
|
925 |
| - adjust_padlen(minlen, vallen, leftjust, &padlen); |
| 949 | + /* If it's infinity or NaN, forget about doing any zero-padding */ |
| 950 | + if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) |
| 951 | + zeropadlen = 0; |
| 952 | + |
| 953 | + adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen); |
926 | 954 |
|
927 | 955 | leading_pad(zpad, &signvalue, &padlen, target);
|
928 | 956 |
|
929 |
| - dostr(convert, vallen, target); |
| 957 | + if (zeropadlen > 0) |
| 958 | + { |
| 959 | + /* If 'e' or 'E' format, inject zeroes before the exponent */ |
| 960 | + char *epos = strrchr(convert, 'e'); |
| 961 | + |
| 962 | + if (!epos) |
| 963 | + epos = strrchr(convert, 'E'); |
| 964 | + if (epos) |
| 965 | + { |
| 966 | + /* pad after exponent */ |
| 967 | + dostr(convert, epos - convert, target); |
| 968 | + while (zeropadlen-- > 0) |
| 969 | + dopr_outch('0', target); |
| 970 | + dostr(epos, vallen - (epos - convert), target); |
| 971 | + } |
| 972 | + else |
| 973 | + { |
| 974 | + /* no exponent, pad after the digits */ |
| 975 | + dostr(convert, vallen, target); |
| 976 | + while (zeropadlen-- > 0) |
| 977 | + dopr_outch('0', target); |
| 978 | + } |
| 979 | + } |
| 980 | + else |
| 981 | + { |
| 982 | + /* no zero padding, just emit the number as-is */ |
| 983 | + dostr(convert, vallen, target); |
| 984 | + } |
930 | 985 |
|
931 | 986 | trailing_pad(&padlen, target);
|
932 | 987 | }
|
|
0 commit comments