1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 *
15 * This file is based on work under the following copyright and permission
16 * notice:
17 *
18 *   Copyright (c) 2016 Marc Andrysco
19 *
20 *   Permission is hereby granted, free of charge, to any person obtaining a copy
21 *   of this software and associated documentation files (the "Software"), to deal
22 *   in the Software without restriction, including without limitation the rights
23 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 *   copies of the Software, and to permit persons to whom the Software is
25 *   furnished to do so, subject to the following conditions:
26 *
27 *   The above copyright notice and this permission notice shall be included in all
28 *   copies or substantial portions of the Software.
29 *
30 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36 *   SOFTWARE.
37 */
38
39#include <math.h>
40
41#include "ecma-helpers.h"
42
43/** \addtogroup ecma ECMA
44 * @{
45 *
46 * \addtogroup ecmahelpers Helpers for operations with ECMA data types
47 * @{
48 */
49
50/**
51 * Printing Floating-Point Numbers
52 *
53 * available at http://cseweb.ucsd.edu/~mandrysc/pub/dtoa.pdf
54 */
55
56/**
57 * Floating point format definitions (next float value)
58 */
59#define ECMA_NEXT_FLOAT(value) (nextafter ((value), INFINITY))
60/**
61 * Floating point format definitions (previous float value)
62 */
63#define ECMA_PREV_FLOAT(value) (nextafter ((value), -INFINITY))
64
65/**
66 * Value of epsilon
67 */
68#define ERROL0_EPSILON 0.0000001
69
70/**
71 * High-precision data structure.
72 */
73typedef struct
74{
75  double value; /**< value */
76  double offset; /**< offset */
77} ecma_high_prec_t;
78
79/**
80 * Normalize the number by factoring in the error.
81 */
82static inline void JERRY_ATTR_ALWAYS_INLINE
83ecma_normalize_high_prec_data (ecma_high_prec_t *hp_data_p) /**< [in, out] float pair */
84{
85  double val = hp_data_p->value;
86
87  hp_data_p->value += hp_data_p->offset;
88  hp_data_p->offset += val - hp_data_p->value;
89} /* ecma_normalize_high_prec_data */
90
91/**
92 * Multiply the high-precision number by ten.
93 */
94static inline void JERRY_ATTR_ALWAYS_INLINE
95ecma_multiply_high_prec_by_10 (ecma_high_prec_t *hp_data_p) /**< [in, out] high-precision number */
96{
97  double value = hp_data_p->value;
98
99  hp_data_p->value *= 10.0;
100  hp_data_p->offset *= 10.0;
101
102  double offset = hp_data_p->value;
103
104  offset -= value * 8.0;
105  offset -= value * 2.0;
106
107  hp_data_p->offset -= offset;
108
109  ecma_normalize_high_prec_data (hp_data_p);
110} /* ecma_multiply_high_prec_by_10 */
111
112/**
113 * Divide the high-precision number by ten.
114 */
115static void
116ecma_divide_high_prec_by_10 (ecma_high_prec_t *hp_data_p) /**< [in, out] high-precision number */
117{
118  double value = hp_data_p->value;
119
120  hp_data_p->value /= 10.0;
121  hp_data_p->offset /= 10.0;
122
123  value -= hp_data_p->value * 8.0;
124  value -= hp_data_p->value * 2.0;
125
126  hp_data_p->offset += value / 10.0;
127
128  ecma_normalize_high_prec_data (hp_data_p);
129} /* ecma_divide_high_prec_by_10 */
130
131/**
132 * Errol0 double to ASCII conversion, guaranteed correct but possibly not optimal.
133 *
134 * @return number of generated digits
135 */
136inline lit_utf8_size_t JERRY_ATTR_ALWAYS_INLINE
137ecma_errol0_dtoa (double val, /**< ecma number */
138                  lit_utf8_byte_t *buffer_p, /**< buffer to generate digits into */
139                  int32_t *exp_p) /**< [out] exponent */
140{
141  double power_of_10 = 1.0;
142  int32_t exp = 1;
143
144  /* normalize the midpoint */
145  ecma_high_prec_t mid;
146
147  mid.value = val;
148  mid.offset = 0.0;
149
150  while (((mid.value > 10.0) || ((mid.value == 10.0) && (mid.offset >= 0.0))) && (exp < 308))
151  {
152    exp++;
153    ecma_divide_high_prec_by_10 (&mid);
154    power_of_10 /= 10.0;
155  }
156
157  while (((mid.value < 1.0) || ((mid.value == 1.0) && (mid.offset < 0.0))) && (exp > -307))
158  {
159    exp--;
160    ecma_multiply_high_prec_by_10 (&mid);
161    power_of_10 *= 10.0;
162  }
163
164  ecma_high_prec_t high_bound, low_bound;
165
166  high_bound.value = mid.value;
167  high_bound.offset = mid.offset;
168
169  if (ECMA_NEXT_FLOAT (val) != INFINITY)
170  {
171    high_bound.offset += (ECMA_NEXT_FLOAT (val) - val) * power_of_10 / (2.0 + ERROL0_EPSILON);
172  }
173
174  low_bound.value = mid.value;
175  low_bound.offset = mid.offset + (ECMA_PREV_FLOAT (val) - val) * power_of_10 / (2.0 + ERROL0_EPSILON);
176
177  ecma_normalize_high_prec_data (&high_bound);
178  ecma_normalize_high_prec_data (&low_bound);
179
180  /* normalized boundaries */
181
182  while (high_bound.value > 10.0 || (high_bound.value == 10.0 && (high_bound.offset >= 0.0)))
183  {
184    exp++;
185    ecma_divide_high_prec_by_10 (&high_bound);
186    ecma_divide_high_prec_by_10 (&low_bound);
187  }
188
189  while (high_bound.value < 1.0 || (high_bound.value == 1.0 && (high_bound.offset < 0.0)))
190  {
191    exp--;
192    ecma_multiply_high_prec_by_10 (&high_bound);
193    ecma_multiply_high_prec_by_10 (&low_bound);
194  }
195
196  /* digit generation */
197
198  lit_utf8_byte_t *dst_p = buffer_p;
199
200  while (high_bound.value != 0.0 || high_bound.offset != 0.0)
201  {
202    uint8_t high_digit = (uint8_t) high_bound.value;
203
204    if ((high_bound.value == high_digit) && (high_bound.offset < 0))
205    {
206      high_digit = (uint8_t) (high_digit - 1u);
207    }
208
209    uint8_t low_digit = (uint8_t) low_bound.value;
210
211    if ((low_bound.value == low_digit) && (low_bound.offset < 0))
212    {
213      low_digit = (uint8_t) (low_digit - 1u);
214    }
215
216    if (low_digit != high_digit)
217    {
218      break;
219    }
220
221    *dst_p++ = (lit_utf8_byte_t) ('0' + high_digit);
222
223    high_bound.value -= high_digit;
224    ecma_multiply_high_prec_by_10 (&high_bound);
225
226    low_bound.value -= low_digit;
227    ecma_multiply_high_prec_by_10 (&low_bound);
228  }
229
230  double mdig = (high_bound.value + low_bound.value) / 2.0 + 0.5;
231  *dst_p++ = (lit_utf8_byte_t) ('0' + (uint8_t) mdig);
232
233  *exp_p = exp;
234
235  return (lit_utf8_size_t) (dst_p - buffer_p);
236} /* ecma_errol0_dtoa */
237
238/**
239 * @}
240 * @}
241 */
242