APM:Libraries
ftoa_engine.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2005, Dmitry Xmelkov
2  All rights reserved.
3 
4  Rewritten in C by Soren Kuula
5 
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions are met:
8 
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  * Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15  * Neither the name of the copyright holders nor the names of
16  contributors may be used to endorse or promote products derived
17  from this software without specific prior written permission.
18 
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  POSSIBILITY OF SUCH DAMAGE. */
30 
31 #include "ftoa_engine.h"
32 
33 #include <stdint.h>
34 
35 #include <AP_Common/AP_Common.h>
36 #include <AP_HAL/AP_HAL.h>
37 
38 /*
39  * 2^b ~= f * r * 10^e
40  * where
41  * i = b div 8
42  * r = 2^(b mod 8)
43  * f = factorTable[i]
44  * e = exponentTable[i]
45  */
46 static const int8_t exponentTable[32] = {
47  -36, -33, -31, -29, -26, -24, -21, -19,
48  -17, -14, -12, -9, -7, -4, -2, 0,
49  3, 5, 8, 10, 12, 15, 17, 20,
50  22, 24, 27, 29, 32, 34, 36, 39
51 };
52 
53 static const uint32_t factorTable[32] = {
54  2295887404UL,
55  587747175UL,
56  1504632769UL,
57  3851859889UL,
58  986076132UL,
59  2524354897UL,
60  646234854UL,
61  1654361225UL,
62  4235164736UL,
63  1084202172UL,
64  2775557562UL,
65  710542736UL,
66  1818989404UL,
67  465661287UL,
68  1192092896UL,
69  3051757813UL,
70  781250000UL,
71  2000000000UL,
72  512000000UL,
73  1310720000UL,
74  3355443200UL,
75  858993459UL,
76  2199023256UL,
77  562949953UL,
78  1441151881UL,
79  3689348815UL,
80  944473297UL,
81  2417851639UL,
82  618970020UL,
83  1584563250UL,
84  4056481921UL,
85  1038459372UL
86 };
87 
88 int16_t ftoa_engine(float val, char *buf, uint8_t precision, uint8_t maxDecimals)
89 {
90  uint8_t flags;
91 
92  // Bit reinterpretation hacks. This will ONLY work on little endian machines.
93  uint8_t *valbits = (uint8_t*)&val;
94  union {
95  float v;
96  uint32_t u;
97  } x;
98  x.v = val;
99  uint32_t frac = x.u & 0x007fffffUL;
100 
101  if (precision>7) precision=7;
102 
103  // Read the sign, shift the exponent in place and delete it from frac.
104  if (valbits[3] & (1<<7)) flags = FTOA_MINUS; else flags = 0;
105  uint8_t exp = valbits[3]<<1;
106  if(valbits[2] & (1<<7)) exp++; // TODO possible but in case of subnormal
107 
108  // Test for easy cases, zero and NaN
109  if(exp==0 && frac==0) {
110  buf[0] = flags | FTOA_ZERO;
111  uint8_t i;
112  for(i=0; i<=precision; i++) {
113  buf[i+1] = '0';
114  }
115  return 0;
116  }
117 
118  if(exp == 0xff) {
119  if(frac == 0) flags |= FTOA_INF; else flags |= FTOA_NAN;
120  }
121 
122  // The implicit leading 1 is made explicit, except if value subnormal.
123  if (exp != 0) frac |= (1UL<<23);
124 
125  uint8_t idx = exp>>3;
126  int8_t exp10 = exponentTable[idx];
127 
128  // We COULD try making the multiplication in situ, where we make
129  // frac and a 64 bit int overlap in memory and select/weigh the
130  // upper 32 bits that way. For starters, this is less risky:
131  int64_t prod = (int64_t)frac * (int64_t)factorTable[idx];
132 
133  // The expConvFactorTable are factor are correct iff the lower 3 exponent
134  // bits are 1 (=7). Else we need to compensate by divding frac.
135  // If the lower 3 bits are 7 we are right.
136  // If the lower 3 bits are 6 we right-shift once
137  // ..
138  // If the lower 3 bits are 0 we right-shift 7x
139  prod >>= (15-(exp & 7));
140 
141  // Now convert to decimal.
142  uint8_t hadNonzeroDigit = 0; // a flag
143  uint8_t outputIdx = 0;
144  int64_t decimal = 100000000000000ull;
145 
146  do {
147  char digit = '0';
148  while(1) {// find the first nonzero digit or any of the next digits.
149  while ((prod -= decimal) >= 0)
150  digit++;
151  // Now we got too low. Fix it by adding again, once.
152  // it might appear more efficient to check before subtract, or
153  // to save and restore last nonnegative value - but in fact
154  // they take as long time and more space.
155  prod += decimal;
156  decimal /= 10;
157 
158  // If already found a leading nonzero digit, accept zeros.
159  if (hadNonzeroDigit) break;
160 
161  // Else, don't return results with a leading zero! Instead
162  // skip those and decrement exp10 accordingly.
163  if(digit == '0') {
164  exp10--;
165  continue;
166  }
167 
168  hadNonzeroDigit = 1;
169 
170  // Compute how many digits N to output.
171  if(maxDecimals != 0) { // If limiting decimals...
172  int8_t beforeDP = exp10+1; // Digits before point
173  if (beforeDP < 1) beforeDP = 1; // Numbers < 1 should also output at least 1 digit.
174  /*
175  * Below a simpler version of this:
176  int8_t afterDP = outputNum - beforeDP;
177  if (afterDP > maxDecimals-1)
178  afterDP = maxDecimals-1;
179  outputNum = beforeDP + afterDP;
180  */
181  maxDecimals = maxDecimals+beforeDP-1;
182  if (precision > maxDecimals)
183  precision = maxDecimals;
184 
185  } else {
186  precision++; // Output one more digit than the param value.
187  }
188 
189  break;
190  }
191 
192  // Now have a digit.
193  outputIdx++;
194  if(digit < '0' + 10) // normal case.
195  buf[outputIdx] = digit;
196  else {
197  // Abnormal case, write 9s and bail.
198  // We might as well abuse hadNonzeroDigit as counter, it will not be used again.
199  for(hadNonzeroDigit=outputIdx; hadNonzeroDigit>0; hadNonzeroDigit--)
200  buf[hadNonzeroDigit] = '9';
201  goto roundup; // this is ugly but it _is_ code derived from assembler :)
202  }
203  } while (outputIdx<precision);
204 
205  // Rounding:
206  decimal *= 10;
207 
208  if (prod - (decimal >> 1) >= 0) {
209 
210  roundup:
211  // Increment digit, cascade
212  while(outputIdx != 0) {
213  if(++buf[outputIdx] == '0' + 10) {
214  if(outputIdx == 1) {
215  buf[outputIdx] = '1';
216  exp10++;
217  flags |= FTOA_CARRY;
218  break;
219  } else
220  buf[outputIdx--] = '0'; // and the loop continues, carrying to next digit.
221  }
222  else break;
223  }
224  }
225 
226  buf[0] = flags;
227  return exp10;
228 }
229 
#define FTOA_NAN
Definition: ftoa_engine.h:42
#define FTOA_MINUS
Definition: ftoa_engine.h:39
#define FTOA_ZERO
Definition: ftoa_engine.h:40
#define x(i)
int16_t ftoa_engine(float val, char *buf, uint8_t precision, uint8_t maxDecimals)
Definition: ftoa_engine.cpp:88
static const uint32_t factorTable[32]
Definition: ftoa_engine.cpp:53
float v
Definition: Printf.cpp:15
#define FTOA_CARRY
Definition: ftoa_engine.h:43
Common definitions and utility routines for the ArduPilot libraries.
static const int8_t exponentTable[32]
Definition: ftoa_engine.cpp:46
#define FTOA_INF
Definition: ftoa_engine.h:41