1 /*
2  * This file is in the Public Domain
3  *
4  * Based on code from Public Domain snprintf.c from mutt
5  *   http://dev.mutt.org/hg/mutt/file/55cd4cb611d9/snprintf.c
6  * Tue Aug 08 22:49:12 2006 +0000
7  *
8  */
9 
10 #ifdef HAVE_CONFIG_H
11 #include <raptor_config.h>
12 #endif
13 
14 #include "raptor.h"
15 #include "raptor_internal.h"
16 
17 #include <float.h>
18 #define __USE_ISOC99 1
19 #include <math.h>
20 
21 #ifndef HAVE_ROUND
22 /* round (C99): round x to the nearest integer, away from zero */
23 #define round(x) (((x) < 0) ? (long)((x)-0.5) : (long)((x)+0.5))
24 #endif
25 
26 #ifndef HAVE_TRUNC
27 /* trunc (C99): round x to the nearest integer, towards zero */
28 #define trunc(x) (((x) < 0) ? ceil((x)) : floor((x)))
29 #endif
30 
31 /* Convert a double to xsd:decimal representation.
32  * Returned is a pointer to the first character of the number
33  * in buffer (don't free it).
34  */
35 char*
raptor_format_float(char * buffer,size_t * currlen,size_t maxlen,double fvalue,unsigned int min,unsigned int max,int flags)36 raptor_format_float(char *buffer, size_t *currlen, size_t maxlen,
37                     double fvalue, unsigned int min, unsigned int max,
38                     int flags)
39 {
40   /* DBL_EPSILON = 52 digits */
41   #define FRAC_MAX_LEN 52
42 
43   double ufvalue;
44   double intpart;
45   double fracpart = 0;
46   double frac;
47   double frac_delta = 10;
48   double mod_10;
49   size_t exp_len;
50   size_t frac_len = 0;
51   size_t idx;
52 
53   if (max < min)
54     max = min;
55 
56   /* index to the last char */
57   idx = maxlen - 1;
58 
59   buffer[idx--] = '\0';
60 
61   ufvalue = fabs (fvalue);
62   intpart = round(ufvalue);
63 
64   /* We "cheat" by converting the fractional part to integer by
65    * multiplying by a factor of 10
66    */
67 
68 
69   frac = (ufvalue - intpart);
70 
71   for (exp_len=0; exp_len <= max; ++exp_len) {
72     frac *= 10;
73 
74     mod_10 = trunc(fmod(trunc(frac), 10));
75 
76     if (fabs(frac_delta - (fracpart / pow(10, exp_len))) < (DBL_EPSILON * 2.0)) {
77       break;
78     }
79 
80     frac_delta = fracpart / pow(10, exp_len);
81 
82     /* Only "append" (numerically) if digit is not a zero */
83     if (mod_10 > 0 && mod_10 < 10) {
84         fracpart = round(frac);
85         frac_len = exp_len;
86     }
87   }
88 
89   if (frac_len < min) {
90     buffer[idx--] = '0';
91   } else {
92     /* Convert/write fractional part (right to left) */
93     do {
94       mod_10 = fmod(trunc(fracpart), 10);
95       --frac_len;
96 
97       buffer[idx--] = "0123456789"[(unsigned)mod_10];
98       fracpart /= 10;
99 
100     } while(fracpart > 1 && (frac_len + 1) > 0);
101   }
102 
103   buffer[idx--] = '.';
104 
105   /* Convert/write integer part (right to left) */
106   do {
107     buffer[idx--] = "0123456789"[(int)fmod(intpart, 10)];
108     intpart /= 10;
109   } while(round(intpart));
110 
111   /* Write a sign, if requested */
112   if(fvalue < 0)
113     buffer[idx--] = '-';
114   else if(flags)
115     buffer[idx--] = '+';
116 
117   *currlen = maxlen - idx - 2;
118   return buffer + idx + 1;
119 }
120