fpconv.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /* fpconv - Floating point conversion routines
  2. *
  3. * Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining
  6. * a copy of this software and associated documentation files (the
  7. * "Software"), to deal in the Software without restriction, including
  8. * without limitation the rights to use, copy, modify, merge, publish,
  9. * distribute, sublicense, and/or sell copies of the Software, and to
  10. * permit persons to whom the Software is furnished to do so, subject to
  11. * the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be
  14. * included in all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  19. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  20. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  21. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  22. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. */
  24. /* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
  25. * with locale support will break when the decimal separator is a comma.
  26. *
  27. * fpconv_* will around these issues with a translation buffer if required.
  28. */
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <assert.h>
  32. #include <string.h>
  33. #include "fpconv.h"
  34. #include "printf.h"
  35. //#define assert(x) ASSERT(x)
  36. /* Lua CJSON assumes the locale is the same for all threads within a
  37. * process and doesn't change after initialisation.
  38. *
  39. * This avoids the need for per thread storage or expensive checks
  40. * for call. */
  41. static char locale_decimal_point = '.';
  42. //#if defined(LUAT_FLOATPOINT_SUPPORT)
  43. /* In theory multibyte decimal_points are possible, but
  44. * Lua CJSON only supports UTF-8 and known locales only have
  45. * single byte decimal points ([.,]).
  46. *
  47. * localconv() may not be thread safe (=>crash), and nl_langinfo() is
  48. * not supported on some platforms. Use sprintf() instead - if the
  49. * locale does change, at least Lua CJSON won't crash. */
  50. // static void fpconv_update_locale()
  51. // {
  52. // // char buf[8];
  53. // // snprintf_(buf, sizeof(buf), "%f", 0.5);
  54. // // /* Failing this test might imply the platform has a buggy dtoa
  55. // // * implementation or wide characters */
  56. // // if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
  57. // // fprintf(stderr, "Error: wide characters found or printf() bug.");
  58. // // abort();
  59. // // }
  60. // // locale_decimal_point = buf[1];
  61. // // locale_decimal_point = '.';
  62. // }
  63. /* Check for a valid number character: [-+0-9a-yA-Y.]
  64. * Eg: -0.6e+5, infinity, 0xF0.F0pF0
  65. *
  66. * Used to find the probable end of a number. It doesn't matter if
  67. * invalid characters are counted - strtod() will find the valid
  68. * number if it exists. The risk is that slightly more memory might
  69. * be allocated before a parse error occurs. */
  70. // static inline int valid_number_character(char ch)
  71. // {
  72. // char lower_ch;
  73. // if ('0' <= ch && ch <= '9')
  74. // return 1;
  75. // if (ch == '-' || ch == '+' || ch == '.')
  76. // return 1;
  77. // /* Hex digits, exponent (e), base (p), "infinity",.. */
  78. // lower_ch = ch | 0x20;
  79. // if ('a' <= lower_ch && lower_ch <= 'y')
  80. // return 1;
  81. // return 0;
  82. // }
  83. /* "fmt" must point to a buffer of at least 6 characters */
  84. static void set_number_format(char *fmt, int precision,char mode)
  85. {
  86. int d1, d2, i;
  87. //assert(1 <= precision && precision <= 14);
  88. /* Create printf format (%.14g) from precision */
  89. d1 = precision / 10;
  90. d2 = precision % 10;
  91. fmt[0] = '%';
  92. fmt[1] = '.';
  93. i = 2;
  94. if (d1) {
  95. fmt[i++] = '0' + d1;
  96. }
  97. fmt[i++] = '0' + d2;
  98. fmt[i++] = mode;
  99. fmt[i] = 0;
  100. }
  101. /* Assumes there is always at least 32 characters available in the target buffer */
  102. int fpconv_g_fmt(char *str, double num, int precision)
  103. {
  104. char buf[FPCONV_G_FMT_BUFSIZE];
  105. char fmt[6];
  106. int len;
  107. char *b;
  108. set_number_format(fmt, precision,'g');
  109. /* Pass through when decimal point character is dot. */
  110. if (locale_decimal_point == '.')
  111. return snprintf_(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
  112. /* snprintf_() to a buffer then translate for other decimal point characters */
  113. len = snprintf_(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
  114. /* Copy into target location. Translate decimal point if required */
  115. b = buf;
  116. do {
  117. *str++ = (*b == locale_decimal_point ? '.' : *b);
  118. } while(*b++);
  119. return len;
  120. }
  121. int fpconv_f_fmt(char *str, double num, int precision)
  122. {
  123. char buf[FPCONV_G_FMT_BUFSIZE];
  124. char fmt[6];
  125. int len;
  126. char *b;
  127. set_number_format(fmt, precision,'f');
  128. /* snprintf_() to a buffer then translate for other decimal point characters */
  129. len = snprintf_(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
  130. /* Copy into target location. Translate decimal point if required */
  131. b = buf;
  132. int i = 0;
  133. int point = 0;
  134. int zero = 0;
  135. for(i = 0; i < len; ++i)
  136. {
  137. if( *(b+i) == locale_decimal_point )
  138. {
  139. point = i;
  140. ++zero;
  141. }
  142. else
  143. {
  144. if( *(b+i) == '0' && point != 0 )
  145. {
  146. ++zero;
  147. }
  148. else
  149. {
  150. zero = 0;
  151. }
  152. }
  153. }
  154. len = len-zero;
  155. for(i = 0; i < len; ++i)
  156. {
  157. *str++ = (*b == locale_decimal_point ? '.' : *b);
  158. *b = *b + 1;
  159. }
  160. *str = 0x00;
  161. return len;
  162. }
  163. // void fpconv_init()
  164. // {
  165. // }
  166. /* vi:ai et sw=4 ts=4:
  167. */