fix8  version 1.4.0
Open Source C++ FIX Framework
modp_numtoa.c
Go to the documentation of this file.
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set expandtab shiftwidth=4 tabstop=4: */
3 
4 /*
5  * Copyright 2005, 2006, 2007
6  * Nick Galbreath -- nickg [at] modp [dot] com
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are
11  * met:
12  *
13  * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in the
18  * documentation and/or other materials provided with the distribution.
19  *
20  * Neither the name of the modp.com nor the names of its
21  * contributors may be used to endorse or promote products derived from
22  * this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  * This is the standard "new" BSD license:
37  * http://www.opensource.org/licenses/bsd-license.php
38  */
39 
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <math.h>
43 #include <fix8/f8dll.h>
44 
45 // other interesting references on num to string convesion
46 // http://www.jb.man.ac.uk/~slowe/cpp/itoa.html
47 // and http://www.ddj.com/dept/cpp/184401596?pgno=6
48 
49 // Version 19-Nov-2007
50 // Fixed round-to-even rules to match printf
51 // thanks to Johannes Otepka
52 
57 static const double pow10_[] = {1, 10, 100, 1000, 10000, 100000, 1000000,
58  10000000, 100000000, 1000000000};
59 
60 static void strreverse(char* begin, char* end)
61 {
62  char aux;
63  while (end > begin)
64  aux = *end, *end-- = *begin, *begin++ = aux;
65 }
66 
67 // slighly modified by DD to return target length
68 F8API size_t modp_dtoa(double value, char* str, int prec) // DD
69 {
70  /* if input is larger than thres_max, revert to exponential */
71  const double thres_max = (double)(0x7FFFFFFF);
72 
73  double diff = 0.0;
74  char* wstr = str;
75  int neg = 0;
76  int whole = 0;
77  double tmp = 0.0;
78  uint32_t frac = 0;
79 
80  /* Hacky test for NaN
81  * under -fast-math this won't work, but then you also won't
82  * have correct nan values anyways. The alternative is
83  * to link with libmath (bad) or hack IEEE double bits (bad)
84  */
85  if (! (value == value)) {
86  str[0] = 'n'; str[1] = 'a'; str[2] = 'n'; str[3] = '\0';
87  return 3; // DD
88  }
89 
90  if (prec < 0) {
91  prec = 0;
92  } else if (prec > 9) {
93  /* precision of >= 10 can lead to overflow errors */
94  prec = 9;
95  }
96 
97 
98  /* we'll work in positive values and deal with the
99  negative sign issue later */
100  if (value < 0) {
101  neg = 1;
102  value = -value;
103  }
104 
105  whole = (int) value;
106  tmp = (value - whole) * pow10_[prec];
107  frac = (uint32_t)(tmp);
108  diff = tmp - frac;
109 
110  if (diff > 0.5) {
111  ++frac;
112  /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
113  if (frac >= pow10_[prec]) {
114  frac = 0;
115  ++whole;
116  }
117  } else if (diff == 0.5 && ((frac == 0) || (frac & 1))) {
118  /* if halfway, round up if odd, OR
119  if last digit is 0. That last part is strange */
120  ++frac;
121  }
122 
123  /* for very large numbers switch back to native sprintf for exponentials.
124  anyone want to write code to replace this? */
125  /*
126  normal printf behavior is to print EVERY whole number digit
127  which can be 100s of characters overflowing your buffers == bad
128  */
129  if (value > thres_max)
130  return sprintf(str, "%e", neg ? -value : value); // DD
131 
132  if (prec == 0) {
133  diff = value - whole;
134  if (diff > 0.5) {
135  /* greater than 0.5, round up, e.g. 1.6 -> 2 */
136  ++whole;
137  } else if (diff == 0.5 && (whole & 1)) {
138  /* exactly 0.5 and ODD, then round up */
139  /* 1.5 -> 2, but 2.5 -> 2 */
140  ++whole;
141  }
142  } else { // these mods DD: remove trailing zero in prec (unless there is only one 0)
143  int count = prec, done = 0;
144  // now do fractional part, as an unsigned number
145  do
146  {
147  --count;
148  if (frac % 10)
149  done += (*wstr++ = (char)(48 + (frac % 10)));
150  else if (done)
151  *wstr++ = '0';
152  }
153  while (frac /= 10);
154  // add extra 0s
155  if (!done)
156  *wstr++ = '0';
157  else
158  while (count-- > 0)
159  *wstr++ = '0';
160  // add decimal
161  *wstr++ = '.';
162  }
163 
164  // do whole part
165  // Take care of sign
166  // Conversion. Number is reversed.
167  do
168  *wstr++ = (char)(48 + (whole % 10));
169  while (whole /= 10);
170  if (neg)
171  *wstr++ = '-';
172  *wstr = 0;
173  strreverse(str, wstr-1);
174  return wstr - str; // DD
175 }
176 
static void strreverse(char *begin, char *end)
Definition: modp_numtoa.c:60
#define F8API
Definition: f8dll.h:60
static const double pow10_[]
Definition: modp_numtoa.c:57
F8API size_t modp_dtoa(double value, char *str, int prec)
Convert double to ascii.
Definition: modp_numtoa.c:68