You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

456 lines
11KB

  1. /* FatLib Library
  2. * Copyright (C) 2013 by William Greiman
  3. *
  4. * This file is part of the FatLib Library
  5. *
  6. * This Library is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This Library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with the FatLib Library. If not, see
  18. * <http://www.gnu.org/licenses/>.
  19. */
  20. #include "FmtNumber.h"
  21. // Use Stimmer div/mod 10 on avr
  22. #ifdef __AVR__
  23. #include <avr/pgmspace.h>
  24. #define USE_STIMMER
  25. #endif // __AVR__
  26. //------------------------------------------------------------------------------
  27. // Stimmer div/mod 10 for AVR
  28. // this code fragment works out i/10 and i%10 by calculating
  29. // i*(51/256)*(256/255)/2 == i*51/510 == i/10
  30. // by "j.k" I mean 32.8 fixed point, j is integer part, k is fractional part
  31. // j.k = ((j+1.0)*51.0)/256.0
  32. // (we add 1 because we will be using the floor of the result later)
  33. // divmod10_asm16 and divmod10_asm32 are public domain code by Stimmer.
  34. // http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679
  35. #define divmod10_asm16(in32, mod8, tmp8) \
  36. asm volatile( \
  37. " ldi %2,51 \n\t" \
  38. " mul %A0,%2 \n\t" \
  39. " clr %A0 \n\t" \
  40. " add r0,%2 \n\t" \
  41. " adc %A0,r1 \n\t" \
  42. " mov %1,r0 \n\t" \
  43. " mul %B0,%2 \n\t" \
  44. " clr %B0 \n\t" \
  45. " add %A0,r0 \n\t" \
  46. " adc %B0,r1 \n\t" \
  47. " clr r1 \n\t" \
  48. " add %1,%A0 \n\t" \
  49. " adc %A0,%B0 \n\t" \
  50. " adc %B0,r1 \n\t" \
  51. " add %1,%B0 \n\t" \
  52. " adc %A0,r1 \n\t" \
  53. " adc %B0,r1 \n\t" \
  54. " lsr %B0 \n\t" \
  55. " ror %A0 \n\t" \
  56. " ror %1 \n\t" \
  57. " ldi %2,10 \n\t" \
  58. " mul %1,%2 \n\t" \
  59. " mov %1,r1 \n\t" \
  60. " clr r1 \n\t" \
  61. :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0")
  62. #define divmod10_asm32(in32, mod8, tmp8) \
  63. asm volatile( \
  64. " ldi %2,51 \n\t" \
  65. " mul %A0,%2 \n\t" \
  66. " clr %A0 \n\t" \
  67. " add r0,%2 \n\t" \
  68. " adc %A0,r1 \n\t" \
  69. " mov %1,r0 \n\t" \
  70. " mul %B0,%2 \n\t" \
  71. " clr %B0 \n\t" \
  72. " add %A0,r0 \n\t" \
  73. " adc %B0,r1 \n\t" \
  74. " mul %C0,%2 \n\t" \
  75. " clr %C0 \n\t" \
  76. " add %B0,r0 \n\t" \
  77. " adc %C0,r1 \n\t" \
  78. " mul %D0,%2 \n\t" \
  79. " clr %D0 \n\t" \
  80. " add %C0,r0 \n\t" \
  81. " adc %D0,r1 \n\t" \
  82. " clr r1 \n\t" \
  83. " add %1,%A0 \n\t" \
  84. " adc %A0,%B0 \n\t" \
  85. " adc %B0,%C0 \n\t" \
  86. " adc %C0,%D0 \n\t" \
  87. " adc %D0,r1 \n\t" \
  88. " add %1,%B0 \n\t" \
  89. " adc %A0,%C0 \n\t" \
  90. " adc %B0,%D0 \n\t" \
  91. " adc %C0,r1 \n\t" \
  92. " adc %D0,r1 \n\t" \
  93. " add %1,%D0 \n\t" \
  94. " adc %A0,r1 \n\t" \
  95. " adc %B0,r1 \n\t" \
  96. " adc %C0,r1 \n\t" \
  97. " adc %D0,r1 \n\t" \
  98. " lsr %D0 \n\t" \
  99. " ror %C0 \n\t" \
  100. " ror %B0 \n\t" \
  101. " ror %A0 \n\t" \
  102. " ror %1 \n\t" \
  103. " ldi %2,10 \n\t" \
  104. " mul %1,%2 \n\t" \
  105. " mov %1,r1 \n\t" \
  106. " clr r1 \n\t" \
  107. :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0")
  108. //------------------------------------------------------------------------------
  109. /*
  110. // C++ code is based on this version of divmod10 by robtillaart.
  111. // http://forum.arduino.cc/index.php?topic=167414.msg1246851#msg1246851
  112. // from robtillaart post:
  113. // The code is based upon the divu10() code from the book Hackers Delight1.
  114. // My insight was that the error formula in divu10() was in fact modulo 10
  115. // but not always. Sometimes it was 10 more.
  116. void divmod10(uint32_t in, uint32_t &div, uint32_t &mod)
  117. {
  118. // q = in * 0.8;
  119. uint32_t q = (in >> 1) + (in >> 2);
  120. q = q + (q >> 4);
  121. q = q + (q >> 8);
  122. q = q + (q >> 16); // not needed for 16 bit version
  123. // q = q / 8; ==> q = in *0.1;
  124. q = q >> 3;
  125. // determine error
  126. uint32_t r = in - ((q << 3) + (q << 1)); // r = in - q*10;
  127. div = q + (r > 9);
  128. if (r > 9) mod = r - 10;
  129. else mod = r;
  130. }
  131. // Hackers delight function is here:
  132. // http://www.hackersdelight.org/hdcodetxt/divuc.c.txt
  133. // Code below uses 8/10 = 0.1100 1100 1100 1100 1100 1100 1100 1100.
  134. // 15 ops including the multiply, or 17 elementary ops.
  135. unsigned divu10(unsigned n) {
  136. unsigned q, r;
  137. q = (n >> 1) + (n >> 2);
  138. q = q + (q >> 4);
  139. q = q + (q >> 8);
  140. q = q + (q >> 16);
  141. q = q >> 3;
  142. r = n - q*10;
  143. return q + ((r + 6) >> 4);
  144. // return q + (r > 9);
  145. }
  146. */
  147. //------------------------------------------------------------------------------
  148. #ifndef DOXYGEN_SHOULD_SKIP_THIS
  149. #ifdef __AVR__
  150. static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32};
  151. static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32};
  152. #else // __AVR__
  153. static const float m[] = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32};
  154. static const float p[] = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32};
  155. #endif // __AVR__
  156. #endif // DOXYGEN_SHOULD_SKIP_THIS
  157. // scale float v by power of ten. return v*10^n
  158. float scale10(float v, int8_t n) {
  159. const float *s;
  160. if (n < 0) {
  161. n = -n;
  162. s = m;
  163. } else {
  164. s = p;
  165. }
  166. n &= 63;
  167. for (uint8_t i = 0; n; n >>= 1, i++) {
  168. #ifdef __AVR__
  169. if (n & 1) {
  170. v *= pgm_read_float(&s[i]);
  171. }
  172. #else // __AVR__
  173. if (n & 1) {
  174. v *= s[i];
  175. }
  176. #endif // __AVR__
  177. }
  178. return v;
  179. }
  180. //------------------------------------------------------------------------------
  181. // Format 16-bit unsigned
  182. char* fmtDec(uint16_t n, char* p) {
  183. while (n > 9) {
  184. #ifdef USE_STIMMER
  185. uint8_t tmp8, r;
  186. divmod10_asm16(n, r, tmp8);
  187. #else // USE_STIMMER
  188. uint16_t t = n;
  189. n = (n >> 1) + (n >> 2);
  190. n = n + (n >> 4);
  191. n = n + (n >> 8);
  192. // n = n + (n >> 16); // no code for 16-bit n
  193. n = n >> 3;
  194. uint8_t r = t - (((n << 2) + n) << 1);
  195. if (r > 9) {
  196. n++;
  197. r -= 10;
  198. }
  199. #endif // USE_STIMMER
  200. *--p = r + '0';
  201. }
  202. *--p = n + '0';
  203. return p;
  204. }
  205. //------------------------------------------------------------------------------
  206. // format 32-bit unsigned
  207. char* fmtDec(uint32_t n, char* p) {
  208. while (n >> 16) {
  209. #ifdef USE_STIMMER
  210. uint8_t tmp8, r;
  211. divmod10_asm32(n, r, tmp8);
  212. #else // USE_STIMMER
  213. uint32_t t = n;
  214. n = (n >> 1) + (n >> 2);
  215. n = n + (n >> 4);
  216. n = n + (n >> 8);
  217. n = n + (n >> 16);
  218. n = n >> 3;
  219. uint8_t r = t - (((n << 2) + n) << 1);
  220. if (r > 9) {
  221. n++;
  222. r -= 10;
  223. }
  224. #endif // USE_STIMMER
  225. *--p = r + '0';
  226. }
  227. return fmtDec((uint16_t)n, p);
  228. }
  229. //------------------------------------------------------------------------------
  230. char* fmtFloat(float value, char* p, uint8_t prec) {
  231. char sign = value < 0 ? '-' : 0;
  232. if (sign) {
  233. value = -value;
  234. }
  235. if (isnan(value)) {
  236. *--p = 'n';
  237. *--p = 'a';
  238. *--p = 'n';
  239. return p;
  240. }
  241. if (isinf(value)) {
  242. *--p = 'f';
  243. *--p = 'n';
  244. *--p = 'i';
  245. return p;
  246. }
  247. if (value > 4294967040.0) {
  248. *--p = 'f';
  249. *--p = 'v';
  250. *--p = 'o';
  251. return p;
  252. }
  253. if (prec > 9) {
  254. prec = 9;
  255. }
  256. value += scale10(0.5, -prec);
  257. uint32_t whole = value;
  258. if (prec) {
  259. char* tmp = p - prec;
  260. uint32_t fraction = scale10(value - whole, prec);
  261. p = fmtDec(fraction, p);
  262. while (p > tmp) {
  263. *--p = '0';
  264. }
  265. *--p = '.';
  266. }
  267. p = fmtDec(whole, p);
  268. if (sign) {
  269. *--p = sign;
  270. }
  271. return p;
  272. }
  273. //------------------------------------------------------------------------------
  274. /** Print a number followed by a field terminator.
  275. * \param[in] value The number to be printed.
  276. * \param[in] ptr Pointer to last char in buffer.
  277. * \param[in] prec Number of digits after decimal point.
  278. * \param[in] expChar Use exp format if non zero.
  279. * \return Pointer to first character of result.
  280. */
  281. char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
  282. bool neg = value < 0;
  283. if (neg) {
  284. value = -value;
  285. }
  286. // check for nan inf ovf
  287. if (isnan(value)) {
  288. *--ptr = 'n';
  289. *--ptr = 'a';
  290. *--ptr = 'n';
  291. return ptr;
  292. }
  293. if (isinf(value)) {
  294. *--ptr = 'f';
  295. *--ptr = 'n';
  296. *--ptr = 'i';
  297. return ptr;
  298. }
  299. if (!expChar && value > 4294967040.0) {
  300. *--ptr = 'f';
  301. *--ptr = 'v';
  302. *--ptr = 'o';
  303. return ptr;
  304. }
  305. if (prec > 9) {
  306. prec = 9;
  307. }
  308. float round = scale10(0.5, -prec);
  309. if (expChar) {
  310. int8_t exp = 0;
  311. bool expNeg = false;
  312. if (value) {
  313. while (value > 10.0) {
  314. value *= 0.1;
  315. exp++;
  316. }
  317. while (value < 1.0) {
  318. value *= 10.0;
  319. exp--;
  320. }
  321. value += round;
  322. if (value > 10.0) {
  323. value *= 0.1;
  324. exp++;
  325. }
  326. expNeg = exp < 0;
  327. if (expNeg) {
  328. exp = -exp;
  329. }
  330. }
  331. ptr = fmtDec((uint16_t)exp, ptr);
  332. if (exp < 10) {
  333. *--ptr = '0';
  334. }
  335. *--ptr = expNeg ? '-' : '+';
  336. *--ptr = expChar;
  337. } else {
  338. // round value
  339. value += round;
  340. }
  341. uint32_t whole = value;
  342. if (prec) {
  343. char* tmp = ptr - prec;
  344. uint32_t fraction = scale10(value - whole, prec);
  345. ptr = fmtDec(fraction, ptr);
  346. while (ptr > tmp) {
  347. *--ptr = '0';
  348. }
  349. *--ptr = '.';
  350. }
  351. ptr = fmtDec(whole, ptr);
  352. if (neg) {
  353. *--ptr = '-';
  354. }
  355. return ptr;
  356. }
  357. //------------------------------------------------------------------------------
  358. char* fmtHex(uint32_t n, char* p) {
  359. do {
  360. uint8_t h = n & 0XF;
  361. *--p = h + (h < 10 ? '0' : 'A' - 10);
  362. n >>= 4;
  363. } while (n);
  364. return p;
  365. }
  366. //------------------------------------------------------------------------------
  367. float scanFloat(const char* str, char** ptr) {
  368. int16_t const EXP_LIMIT = 100;
  369. bool digit = false;
  370. bool dot = false;
  371. uint32_t fract = 0;
  372. int fracExp = 0;
  373. uint8_t nd = 0;
  374. bool neg;
  375. int c;
  376. float v;
  377. const char* successPtr = str;
  378. if (ptr) {
  379. *ptr = const_cast<char*>(str);
  380. }
  381. while (isSpace((c = *str++))) {}
  382. neg = c == '-';
  383. if (c == '-' || c == '+') {
  384. c = *str++;
  385. }
  386. // Skip leading zeros
  387. while (c == '0') {
  388. c = *str++;
  389. digit = true;
  390. }
  391. for (;;) {
  392. if (isDigit(c)) {
  393. digit = true;
  394. if (nd < 9) {
  395. fract = 10*fract + c - '0';
  396. nd++;
  397. if (dot) {
  398. fracExp--;
  399. }
  400. } else {
  401. if (!dot) {
  402. fracExp++;
  403. }
  404. }
  405. } else if (c == '.') {
  406. if (dot) {
  407. goto fail;
  408. }
  409. dot = true;
  410. } else {
  411. if (!digit) {
  412. goto fail;
  413. }
  414. break;
  415. }
  416. successPtr = str;
  417. c = *str++;
  418. }
  419. if (c == 'e' || c == 'E') {
  420. int exp = 0;
  421. c = *str++;
  422. bool expNeg = c == '-';
  423. if (c == '-' || c == '+') {
  424. c = *str++;
  425. }
  426. while (isDigit(c)) {
  427. if (exp > EXP_LIMIT) {
  428. goto fail;
  429. }
  430. exp = 10*exp + c - '0';
  431. successPtr = str;
  432. c = *str++;
  433. }
  434. fracExp += expNeg ? -exp : exp;
  435. }
  436. if (ptr) {
  437. *ptr = const_cast<char*>(successPtr);
  438. }
  439. v = scale10(static_cast<float>(fract), fracExp);
  440. return neg ? -v : v;
  441. fail:
  442. return 0;
  443. }