dtostr.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*
  2. * Copyright (C) 2000, 2001 Manuel Novoa III
  3. *
  4. * Function: int __dtostr(FILE * fp, size_t size, double x,
  5. * char flag[], int width, int preci, char mode)
  6. *
  7. * This was written for uClibc to provide floating point support for
  8. * the printf functions. It handles +/- infinity and nan on i386.
  9. *
  10. * Notes:
  11. *
  12. * It should also be fairly portable, as not assumptions are made about the
  13. * bit-layout of doubles.
  14. *
  15. * It should be too difficult to convert this to handle long doubles on i386.
  16. * For information, see the comments below.
  17. *
  18. * TODO:
  19. * long double and/or float version? (note: for float can trim code some).
  20. *
  21. * Decrease the size. This is really much bigger than I'd like.
  22. */
  23. /*****************************************************************************/
  24. /* Don't change anything that follows unless you know what you're doing. */
  25. /*****************************************************************************/
  26. /*
  27. * Configuration for the scaling power table. Ignoring denormals, you
  28. * should have 2**EXP_TABLE_SIZE >= MAX_DBL_EXP >= 2**(EXP_TABLE_SIZE-1).
  29. * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices.
  30. */
  31. #define EXP_TABLE_SIZE 9
  32. /*
  33. * Set this to the maximum number of digits you want converted.
  34. * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits.
  35. * 17 digits suffices to uniquely determine a double on i386.
  36. */
  37. #define MAX_DIGITS 17
  38. /*
  39. * Set this to the smallest integer type capable of storing a pointer.
  40. */
  41. #define INT_OR_PTR int
  42. /*
  43. * This is really only used to check for infinities. The macro produces
  44. * smaller code for i386 and, since this is tested before any floating point
  45. * calculations, it doesn't appear to suffer from the excess precision problem
  46. * caused by the FPU that strtod had. If it causes problems, call the function
  47. * and compile zoicheck.c with -ffloat-store.
  48. */
  49. #if 1
  50. #define _zero_or_inf_check(x) ( x == (x/4) )
  51. #else
  52. extern int _zero_or_inf_check(double x);
  53. #endif
  54. /*
  55. * Fairly portable nan check. Bitwise for i386 generated larger code.
  56. * If you have a better version, comment this out.
  57. */
  58. #define isnan(x) (x != x)
  59. /*****************************************************************************/
  60. /* Don't change anything that follows peroid!!! ;-) */
  61. /*****************************************************************************/
  62. #include <stdio.h>
  63. #include <string.h>
  64. #include <assert.h>
  65. #include <float.h>
  66. #include <limits.h>
  67. extern int fnprintf(FILE * fp, size_t size, const char *fmt, ...);
  68. /* from printf.c -- should really be in an internal header file */
  69. enum {
  70. FLAG_PLUS = 0,
  71. FLAG_MINUS_LJUSTIFY,
  72. FLAG_HASH,
  73. FLAG_0_PAD,
  74. FLAG_SPACE,
  75. };
  76. /*****************************************************************************/
  77. /*
  78. * Set things up for the scaling power table.
  79. */
  80. #if EXP_TABLE_SIZE < 6
  81. #error EXP_TABLE_SIZE should be at least 6 to comply with standards
  82. #endif
  83. #define EXP_TABLE_MAX (1U<<(EXP_TABLE_SIZE-1))
  84. /*
  85. * Only bother checking if this is too small.
  86. * Throw in some play for denormals ( roughly O(-324) vs O(-307) on i386 ).
  87. */
  88. #if (3+DBL_DIG-DBL_MIN_10_EXP)/2 > EXP_TABLE_MAX
  89. #error larger EXP_TABLE_SIZE needed
  90. #endif
  91. /*
  92. * With 32 bit ints, we can get 9 digits per block.
  93. */
  94. #define DIGITS_PER_BLOCK 9
  95. #if (INT_MAX >> 30)
  96. #define DIGIT_BLOCK_TYPE int
  97. #define DB_FMT "%.*d"
  98. #elif (LONG_MAX >> 30)
  99. #define DIGIT_BLOCK_TYPE long
  100. #define DB_FMT "%.*ld"
  101. #else
  102. #error need at least 32 bit longs
  103. #endif
  104. /* Are there actually any machines where this might fail? */
  105. #if 'A' > 'a'
  106. #error ordering assumption violated : 'A' > 'a'
  107. #endif
  108. /* Maximum number of calls to fnprintf to output double. */
  109. #define MAX_CALLS 8
  110. /*****************************************************************************/
  111. #define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
  112. /* extra space for '-', '.', 'e+###', and nul */
  113. #define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
  114. /*****************************************************************************/
  115. static const char *fmts[] = {
  116. "%0*d", "%.*s", ".", "inf", "INF", "nan", "NAN", "%*s"
  117. };
  118. /*****************************************************************************/
  119. int __dtostr(FILE * fp, size_t size, double x,
  120. char flag[], int width, int preci, char mode)
  121. {
  122. double exp_table[EXP_TABLE_SIZE];
  123. double p10;
  124. DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */
  125. int i, j;
  126. int round, o_exp;
  127. int exp, exp_neg;
  128. char *s;
  129. char *e;
  130. char buf[BUF_SIZE];
  131. char buf2[BUF_SIZE];
  132. INT_OR_PTR pc_fwi[2*MAX_CALLS];
  133. INT_OR_PTR *ppc;
  134. char exp_buf[8];
  135. char drvr[8];
  136. char *pdrvr;
  137. int npc;
  138. int cnt;
  139. char sign_str[2];
  140. char o_mode;
  141. /* check that INT_OR_PTR is sufficiently large */
  142. assert( sizeof(INT_OR_PTR) == sizeof(char *) );
  143. *sign_str = flag[FLAG_PLUS];
  144. *(sign_str+1) = 0;
  145. if (isnan(x)) { /* nan check */
  146. pdrvr = drvr + 1;
  147. *pdrvr++ = 5 + (mode < 'a');
  148. pc_fwi[2] = 3;
  149. flag[FLAG_0_PAD] = 0;
  150. goto EXIT_SPECIAL;
  151. }
  152. if (x == 0) { /* handle 0 now to avoid false positive */
  153. exp = -1;
  154. goto GENERATE_DIGITS;
  155. }
  156. if (x < 0) { /* convert negatives to positives */
  157. *sign_str = '-';
  158. x = -x;
  159. }
  160. if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */
  161. pdrvr = drvr + 1;
  162. *pdrvr++ = 3 + + (mode < 'a');
  163. pc_fwi[2] = 3;
  164. flag[FLAG_0_PAD] = 0;
  165. goto EXIT_SPECIAL;
  166. }
  167. /* need to build the scaling table */
  168. for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) {
  169. exp_table[i] = p10;
  170. p10 *= p10;
  171. }
  172. exp_neg = 0;
  173. if (x < 1e8) { /* do we need to scale up or down? */
  174. exp_neg = 1;
  175. }
  176. exp = DIGITS_PER_BLOCK - 1;
  177. i = EXP_TABLE_SIZE;
  178. j = EXP_TABLE_MAX;
  179. while ( i-- ) { /* scale x such that 1e8 <= x < 1e9 */
  180. if (exp_neg) {
  181. if (x * exp_table[i] < 1e9) {
  182. x *= exp_table[i];
  183. exp -= j;
  184. }
  185. } else {
  186. if (x / exp_table[i] >= 1e8) {
  187. x /= exp_table[i];
  188. exp += j;
  189. }
  190. }
  191. j >>= 1;
  192. }
  193. if (x >= 1e9) { /* handle bad rounding case */
  194. x /= 10;
  195. ++exp;
  196. }
  197. assert(x < 1e9);
  198. GENERATE_DIGITS:
  199. s = buf2 + 2; /* leave space for '\0' and '0' */
  200. for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) {
  201. digit_block = (DIGIT_BLOCK_TYPE) x;
  202. x = (x - digit_block) * 1e9;
  203. s += sprintf(s, DB_FMT, DIGITS_PER_BLOCK, digit_block);
  204. }
  205. /*************************************************************************/
  206. *exp_buf = 'e';
  207. if (mode < 'a') {
  208. *exp_buf = 'E';
  209. mode += ('a' - 'A');
  210. }
  211. o_mode = mode;
  212. round = preci;
  213. if ((mode == 'g') && (round > 0)){
  214. --round;
  215. }
  216. if (mode == 'f') {
  217. round += exp;
  218. }
  219. RESTART:
  220. memcpy(buf,buf2,sizeof(buf2)); /* backup in case g need to be f */
  221. s = buf;
  222. *s++ = 0; /* terminator for rounding and 0-triming */
  223. *s = '0'; /* space to round */
  224. i = 0;
  225. e = s + MAX_DIGITS + 1;
  226. if (round < MAX_DIGITS) {
  227. e = s + round + 2;
  228. if (*e >= '5') {
  229. i = 1;
  230. }
  231. }
  232. do { /* handle rounding and trim trailing 0s */
  233. *--e += i; /* add the carry */
  234. } while ((*e == '0') || (*e > '9'));
  235. o_exp = exp;
  236. if (e <= s) { /* we carried into extra digit */
  237. ++o_exp;
  238. e = s; /* needed if all 0s */
  239. } else {
  240. ++s;
  241. }
  242. *++e = 0; /* ending nul char */
  243. if ((mode == 'g') && ((o_exp >= -4) && (o_exp < round))) {
  244. mode = 'f';
  245. goto RESTART;
  246. }
  247. exp = o_exp;
  248. if (mode != 'f') {
  249. o_exp = 0;
  250. }
  251. if (o_exp < 0) {
  252. *--s = '0'; /* fake the first digit */
  253. }
  254. pdrvr = drvr+1;
  255. ppc = pc_fwi+2;
  256. *pdrvr++ = 0;
  257. *ppc++ = 1;
  258. *ppc++ = (INT_OR_PTR)(*s++ - '0');
  259. i = e - s; /* total digits */
  260. if (o_exp >= 0) {
  261. if (o_exp >= i) { /* all digit(s) left of decimal */
  262. *pdrvr++ = 1;
  263. *ppc++ = i;
  264. *ppc++ = (INT_OR_PTR)(s);
  265. o_exp -= i;
  266. i = 0;
  267. if (o_exp>0) { /* have 0s left of decimal */
  268. *pdrvr++ = 0;
  269. *ppc++ = o_exp;
  270. *ppc++ = 0;
  271. }
  272. } else if (o_exp > 0) { /* decimal between digits */
  273. *pdrvr++ = 1;
  274. *ppc++ = o_exp;
  275. *ppc++ = (INT_OR_PTR)(s);
  276. s += o_exp;
  277. i -= o_exp;
  278. }
  279. o_exp = -1;
  280. }
  281. if (flag[FLAG_HASH] || (i) || ((o_mode != 'g') && (preci > 0))) {
  282. *pdrvr++ = 2; /* need decimal */
  283. *ppc++ = 1; /* needed for width calc */
  284. ppc++;
  285. }
  286. if (++o_exp < 0) { /* have 0s right of decimal */
  287. *pdrvr++ = 0;
  288. *ppc++ = -o_exp;
  289. *ppc++ = 0;
  290. }
  291. if (i) { /* have digit(s) right of decimal */
  292. *pdrvr++ = 1;
  293. *ppc++ = i;
  294. *ppc++ = (INT_OR_PTR)(s);
  295. }
  296. if (o_mode != 'g') {
  297. i -= o_exp;
  298. if (i < preci) { /* have 0s right of digits */
  299. i = preci - i;
  300. *pdrvr++ = 0;
  301. *ppc++ = i;
  302. *ppc++ = 0;
  303. }
  304. }
  305. /* build exponent string */
  306. if (mode != 'f') {
  307. *pdrvr++ = 1;
  308. *ppc++ = sprintf(exp_buf,"%c%+.2d", *exp_buf, exp);
  309. *ppc++ = (INT_OR_PTR) exp_buf;
  310. }
  311. EXIT_SPECIAL:
  312. npc = pdrvr - drvr;
  313. ppc = pc_fwi + 2;
  314. for (i=1 ; i< npc ; i++) {
  315. width -= *(ppc++);
  316. ppc++;
  317. }
  318. i = 0;
  319. if (*sign_str) {
  320. i = 1;
  321. }
  322. width -= i;
  323. if (width <= 0) {
  324. width = 0;
  325. } else {
  326. if (flag[FLAG_MINUS_LJUSTIFY]) { /* padding on right */
  327. ++npc;
  328. *pdrvr++ = 7;
  329. *ppc = width;
  330. *++ppc = (INT_OR_PTR)("");
  331. width = 0;
  332. } else if (flag[FLAG_0_PAD] == '0') { /* 0 padding */
  333. pc_fwi[2] += width;
  334. width = 0;
  335. }
  336. }
  337. *drvr = 7;
  338. ppc = pc_fwi;
  339. *ppc++ = width + i;
  340. *ppc = (INT_OR_PTR) sign_str;
  341. pdrvr = drvr;
  342. ppc = pc_fwi;
  343. cnt = 0;
  344. for (i=0 ; i<npc ; i++) {
  345. #if 1
  346. fnprintf(fp, size, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)),
  347. (INT_OR_PTR)(*(ppc+1)));
  348. #else
  349. j = fnprintf(fp, size, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)),
  350. (INT_OR_PTR)(*(ppc+1)));
  351. assert(j == *ppc);
  352. #endif
  353. if (size > *ppc) {
  354. size -= *ppc;
  355. }
  356. cnt += *ppc; /* to avoid problems if j == -1 */
  357. ppc += 2;
  358. }
  359. return cnt;
  360. }