fltfmt.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <ctype.h>
  4. #include "fmtdef.h"
  5. enum
  6. {
  7. FDIGIT = 30,
  8. FDEFLT = 6,
  9. NSIGNIF = 17,
  10. };
  11. static int
  12. xadd(char *a, int n, int v)
  13. {
  14. char *b;
  15. int c;
  16. if(n < 0 || n >= NSIGNIF)
  17. return 0;
  18. for(b = a+n; b >= a; b--) {
  19. c = *b + v;
  20. if(c <= '9') {
  21. *b = c;
  22. return 0;
  23. }
  24. *b = '0';
  25. v = 1;
  26. }
  27. *a = '1'; // overflow adding
  28. return 1;
  29. }
  30. static int
  31. xsub(char *a, int n, int v)
  32. {
  33. char *b;
  34. int c;
  35. for(b = a+n; b >= a; b--) {
  36. c = *b - v;
  37. if(c >= '0') {
  38. *b = c;
  39. return 0;
  40. }
  41. *b = '9';
  42. v = 1;
  43. }
  44. *a = '9'; // underflow subtracting
  45. return 1;
  46. }
  47. static void
  48. xdtoa(Fmt *fmt, char *s2, double f)
  49. {
  50. char s1[NSIGNIF+10];
  51. double g, h;
  52. int e, d, i, n;
  53. int c1, c2, c3, c4, ucase, sign, chr, prec;
  54. prec = FDEFLT;
  55. if(fmt->flags & FmtPrec)
  56. prec = fmt->prec;
  57. if(prec > FDIGIT)
  58. prec = FDIGIT;
  59. if(isNaN(f)) {
  60. strcpy(s2, "NaN");
  61. return;
  62. }
  63. if(isInf(f, 1)) {
  64. strcpy(s2, "+Inf");
  65. return;
  66. }
  67. if(isInf(f, -1)) {
  68. strcpy(s2, "-Inf");
  69. return;
  70. }
  71. sign = 0;
  72. if(f < 0) {
  73. f = -f;
  74. sign++;
  75. }
  76. ucase = 0;
  77. chr = fmt->r;
  78. if(isupper(chr)) {
  79. ucase = 1;
  80. chr = tolower(chr);
  81. }
  82. e = 0;
  83. g = f;
  84. if(g != 0) {
  85. frexp(f, &e);
  86. e = e * .301029995664;
  87. if(e >= -150 && e <= +150) {
  88. d = 0;
  89. h = f;
  90. } else {
  91. d = e/2;
  92. h = f * pow10(-d);
  93. }
  94. g = h * pow10(d-e);
  95. while(g < 1) {
  96. e--;
  97. g = h * pow10(d-e);
  98. }
  99. while(g >= 10) {
  100. e++;
  101. g = h * pow10(d-e);
  102. }
  103. }
  104. /*
  105. * convert NSIGNIF digits and convert
  106. * back to get accuracy.
  107. */
  108. for(i=0; i<NSIGNIF; i++) {
  109. d = g;
  110. s1[i] = d + '0';
  111. g = (g - d) * 10;
  112. }
  113. s1[i] = 0;
  114. /*
  115. * try decimal rounding to eliminate 9s
  116. */
  117. c2 = prec + 1;
  118. if(chr == 'f')
  119. c2 += e;
  120. if(c2 >= NSIGNIF-2) {
  121. strcpy(s2, s1);
  122. d = e;
  123. s1[NSIGNIF-2] = '0';
  124. s1[NSIGNIF-1] = '0';
  125. sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
  126. g = strtod(s1, nil);
  127. if(g == f)
  128. goto found;
  129. if(xadd(s1, NSIGNIF-3, 1)) {
  130. e++;
  131. sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
  132. }
  133. g = strtod(s1, nil);
  134. if(g == f)
  135. goto found;
  136. strcpy(s1, s2);
  137. e = d;
  138. }
  139. /*
  140. * convert back so s1 gets exact answer
  141. */
  142. for(;;) {
  143. sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
  144. g = strtod(s1, nil);
  145. if(f > g) {
  146. if(xadd(s1, NSIGNIF-1, 1))
  147. e--;
  148. continue;
  149. }
  150. if(f < g) {
  151. if(xsub(s1, NSIGNIF-1, 1))
  152. e++;
  153. continue;
  154. }
  155. break;
  156. }
  157. found:
  158. /*
  159. * sign
  160. */
  161. d = 0;
  162. i = 0;
  163. if(sign)
  164. s2[d++] = '-';
  165. else if(fmt->flags & FmtSign)
  166. s2[d++] = '+';
  167. else if(fmt->flags & FmtSpace)
  168. s2[d++] = ' ';
  169. /*
  170. * copy into final place
  171. * c1 digits of leading '0'
  172. * c2 digits from conversion
  173. * c3 digits of trailing '0'
  174. * c4 digits after '.'
  175. */
  176. c1 = 0;
  177. c2 = prec + 1;
  178. c3 = 0;
  179. c4 = prec;
  180. switch(chr) {
  181. default:
  182. if(xadd(s1, c2, 5))
  183. e++;
  184. break;
  185. case 'g':
  186. /*
  187. * decide on 'e' of 'f' style convers
  188. */
  189. if(xadd(s1, c2, 5))
  190. e++;
  191. if(e >= -5 && e <= prec) {
  192. c1 = -e - 1;
  193. c4 = prec - e;
  194. chr = 'h'; // flag for 'f' style
  195. }
  196. break;
  197. case 'f':
  198. if(xadd(s1, c2+e, 5))
  199. e++;
  200. c1 = -e;
  201. if(c1 > prec)
  202. c1 = c2;
  203. c2 += e;
  204. break;
  205. }
  206. /*
  207. * clean up c1 c2 and c3
  208. */
  209. if(c1 < 0)
  210. c1 = 0;
  211. if(c2 < 0)
  212. c2 = 0;
  213. if(c2 > NSIGNIF) {
  214. c3 = c2-NSIGNIF;
  215. c2 = NSIGNIF;
  216. }
  217. /*
  218. * copy digits
  219. */
  220. while(c1 > 0) {
  221. if(c1+c2+c3 == c4)
  222. s2[d++] = '.';
  223. s2[d++] = '0';
  224. c1--;
  225. }
  226. while(c2 > 0) {
  227. if(c2+c3 == c4)
  228. s2[d++] = '.';
  229. s2[d++] = s1[i++];
  230. c2--;
  231. }
  232. while(c3 > 0) {
  233. if(c3 == c4)
  234. s2[d++] = '.';
  235. s2[d++] = '0';
  236. c3--;
  237. }
  238. /*
  239. * strip trailing '0' on g conv
  240. */
  241. if(fmt->flags & FmtSharp) {
  242. if(0 == c4)
  243. s2[d++] = '.';
  244. } else
  245. if(chr == 'g' || chr == 'h') {
  246. for(n=d-1; n>=0; n--)
  247. if(s2[n] != '0')
  248. break;
  249. for(i=n; i>=0; i--)
  250. if(s2[i] == '.') {
  251. d = n;
  252. if(i != n)
  253. d++;
  254. break;
  255. }
  256. }
  257. if(chr == 'e' || chr == 'g') {
  258. if(ucase)
  259. s2[d++] = 'E';
  260. else
  261. s2[d++] = 'e';
  262. c1 = e;
  263. if(c1 < 0) {
  264. s2[d++] = '-';
  265. c1 = -c1;
  266. } else
  267. s2[d++] = '+';
  268. if(c1 >= 100) {
  269. s2[d++] = c1/100 + '0';
  270. c1 = c1%100;
  271. }
  272. s2[d++] = c1/10 + '0';
  273. s2[d++] = c1%10 + '0';
  274. }
  275. s2[d] = 0;
  276. }
  277. int
  278. _floatfmt(Fmt *fmt, double f)
  279. {
  280. char s[FDIGIT+10];
  281. xdtoa(fmt, s, f);
  282. fmt->flags &= FmtWidth|FmtLeft;
  283. _fmtcpy(fmt, s, strlen(s), strlen(s));
  284. return 0;
  285. }
  286. int
  287. _efgfmt(Fmt *f)
  288. {
  289. double d;
  290. d = va_arg(f->args, double);
  291. return _floatfmt(f, d);
  292. }