Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

406 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 <avr/pgmspace.h>
  21. #include <FmtNumber.h>
  22. // Use Stimmer div/mod 10 on avr
  23. #ifdef __AVR__
  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. static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32};
  149. static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32};
  150. // scale float v by power of ten. return v*10^n
  151. float scale10(float v, int8_t n) {
  152. const float *s;
  153. if (n < 0) {
  154. n = -n;
  155. s = m;
  156. } else {
  157. s = p;
  158. }
  159. n &= 63;
  160. for (uint8_t i = 0; n; n >>= 1, i++) {
  161. if (n & 1) v *= pgm_read_float(&s[i]);
  162. }
  163. return v;
  164. }
  165. //------------------------------------------------------------------------------
  166. // Format 16-bit unsigned
  167. char* fmtDec(uint16_t n, char* p) {
  168. while (n > 9) {
  169. #ifdef USE_STIMMER
  170. uint8_t tmp8, r;
  171. divmod10_asm16(n, r, tmp8);
  172. #else // USE_STIMMER
  173. uint16_t t = n;
  174. n = (n >> 1) + (n >> 2);
  175. n = n + (n >> 4);
  176. n = n + (n >> 8);
  177. // n = n + (n >> 16); // no code for 16-bit n
  178. n = n >> 3;
  179. uint8_t r = t - (((n << 2) + n) << 1);
  180. if (r > 9) {
  181. n++;
  182. r -= 10;
  183. }
  184. #endif // USE_STIMMER
  185. *--p = r + '0';
  186. }
  187. *--p = n + '0';
  188. return p;
  189. }
  190. //------------------------------------------------------------------------------
  191. // format 32-bit unsigned
  192. char* fmtDec(uint32_t n, char* p) {
  193. while (n >> 16) {
  194. #ifdef USE_STIMMER
  195. uint8_t tmp8, r;
  196. divmod10_asm32(n, r, tmp8);
  197. #else // USE_STIMMER
  198. uint32_t t = n;
  199. n = (n >> 1) + (n >> 2);
  200. n = n + (n >> 4);
  201. n = n + (n >> 8);
  202. n = n + (n >> 16);
  203. n = n >> 3;
  204. uint8_t r = t - (((n << 2) + n) << 1);
  205. if (r > 9) {
  206. n++;
  207. r -= 10;
  208. }
  209. #endif // USE_STIMMER
  210. *--p = r + '0';
  211. }
  212. return fmtDec((uint16_t)n, p);
  213. }
  214. //------------------------------------------------------------------------------
  215. char* fmtFloat(float value, char* p, uint8_t prec) {
  216. char sign = value < 0 ? '-' : 0;
  217. if (sign) value = -value;
  218. if (isnan(value)) {
  219. *--p = 'n';
  220. *--p = 'a';
  221. *--p = 'n';
  222. return p;
  223. }
  224. if (isinf(value)) {
  225. *--p = 'f';
  226. *--p = 'n';
  227. *--p = 'i';
  228. return p;
  229. }
  230. if (value > 4294967040.0) {
  231. *--p = 'f';
  232. *--p = 'v';
  233. *--p = 'o';
  234. return p;
  235. }
  236. if (prec > 9) prec = 9;
  237. value += scale10(0.5, -prec);
  238. uint32_t whole = value;
  239. if (prec) {
  240. char* tmp = p - prec;
  241. uint32_t fraction = scale10(value - whole, prec);
  242. p = fmtDec(fraction, p);
  243. while (p > tmp) *--p = '0';
  244. *--p = '.';
  245. }
  246. p = fmtDec(whole, p);
  247. if (sign) *--p = sign;
  248. return p;
  249. }
  250. //------------------------------------------------------------------------------
  251. /** Print a number followed by a field terminator.
  252. * \param[in] value The number to be printed.
  253. * \param[in] ptr Pointer to last char in buffer.
  254. * \param[in] prec Number of digits after decimal point.
  255. * \param[in] expChar Use exp format if non zero.
  256. * \return Pointer to first character of result.
  257. */
  258. char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
  259. bool neg = value < 0;
  260. if (neg) value = -value;
  261. // check for nan inf ovf
  262. if (isnan(value)) {
  263. *--ptr = 'n';
  264. *--ptr = 'a';
  265. *--ptr = 'n';
  266. return ptr;
  267. }
  268. if (isinf(value)) {
  269. *--ptr = 'f';
  270. *--ptr = 'n';
  271. *--ptr = 'i';
  272. return ptr;
  273. }
  274. if (!expChar && value > 4294967040.0) {
  275. *--ptr = 'f';
  276. *--ptr = 'v';
  277. *--ptr = 'o';
  278. return ptr;
  279. }
  280. if (prec > 9) prec = 9;
  281. float round = scale10(0.5, -prec);
  282. if (expChar) {
  283. int8_t exp = 0;
  284. bool expNeg = false;
  285. if (value) {
  286. while (value > 10.0) {
  287. value *= 0.1;
  288. exp++;
  289. }
  290. while (value < 1.0) {
  291. value *= 10.0;
  292. exp--;
  293. }
  294. value += round;
  295. if (value > 10.0) {
  296. value *= 0.1;
  297. exp++;
  298. }
  299. expNeg = exp < 0;
  300. if (expNeg) exp = -exp;
  301. }
  302. ptr = fmtDec((uint16_t)exp, ptr);
  303. if (exp < 10) *--ptr = '0';
  304. *--ptr = expNeg ? '-' : '+';
  305. *--ptr = expChar;
  306. } else {
  307. // round value
  308. value += round;
  309. }
  310. uint32_t whole = value;
  311. if (prec) {
  312. char* tmp = ptr - prec;
  313. uint32_t fraction = scale10(value - whole, prec);
  314. ptr = fmtDec(fraction, ptr);
  315. while (ptr > tmp) *--ptr = '0';
  316. *--ptr = '.';
  317. }
  318. ptr = fmtDec(whole, ptr);
  319. if (neg) *--ptr = '-';
  320. return ptr;
  321. }
  322. //------------------------------------------------------------------------------
  323. char* fmtHex(uint32_t n, char* p) {
  324. do {
  325. uint8_t h = n & 0XF;
  326. *--p = h + (h < 10 ? '0' : 'A' - 10);
  327. n >>= 4;
  328. } while (n);
  329. return p;
  330. }
  331. //------------------------------------------------------------------------------
  332. float scanFloat(const char* str, char** ptr) {
  333. int16_t const EXP_LIMIT = 100;
  334. bool digit = false;
  335. bool dot = false;
  336. uint32_t fract = 0;
  337. int fracExp = 0;
  338. uint8_t nd = 0;
  339. bool neg;
  340. int c;
  341. float v;
  342. const char* successPtr;
  343. if (ptr) *ptr = const_cast<char*>(str);
  344. while (isspace((c = *str++))) {}
  345. neg = c == '-';
  346. if (c == '-' || c == '+') c = *str++;
  347. // Skip leading zeros
  348. while (c == '0') {
  349. c = *str++;
  350. digit = true;
  351. }
  352. for (;;) {
  353. if (isdigit(c)) {
  354. digit = true;
  355. if (nd < 9) {
  356. fract = 10*fract + c - '0';
  357. nd++;
  358. if (dot) fracExp--;
  359. } else {
  360. if (!dot) fracExp++;
  361. }
  362. } else if (c == '.') {
  363. if (dot) goto fail;
  364. dot = true;
  365. } else {
  366. if (!digit) goto fail;
  367. break;
  368. }
  369. successPtr = str;
  370. c = *str++;
  371. }
  372. if (c == 'e' || c == 'E') {
  373. int exp = 0;
  374. c = *str++;
  375. bool expNeg = c == '-';
  376. if (c == '-' || c == '+') {
  377. c = *str++;
  378. }
  379. while (isdigit(c)) {
  380. if (exp > EXP_LIMIT) goto fail;
  381. exp = 10*exp + c - '0';
  382. successPtr = str;
  383. c = *str++;
  384. }
  385. fracExp += expNeg ? -exp : exp;
  386. }
  387. if (ptr) *ptr = const_cast<char*>(successPtr);
  388. v = scale10(static_cast<float>(fract), fracExp);
  389. return neg ? -v: v;
  390. fail:
  391. return 0;
  392. }