hitokage 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +89 -0
  7. data/Rakefile +19 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/ext/hitokage_ext/double-conversion/AUTHORS +14 -0
  11. data/ext/hitokage_ext/double-conversion/COPYING +26 -0
  12. data/ext/hitokage_ext/double-conversion/LICENSE +26 -0
  13. data/ext/hitokage_ext/double-conversion/README +54 -0
  14. data/ext/hitokage_ext/double-conversion/bignum-dtoa.cc +641 -0
  15. data/ext/hitokage_ext/double-conversion/bignum-dtoa.h +84 -0
  16. data/ext/hitokage_ext/double-conversion/bignum.cc +766 -0
  17. data/ext/hitokage_ext/double-conversion/bignum.h +144 -0
  18. data/ext/hitokage_ext/double-conversion/cached-powers.cc +175 -0
  19. data/ext/hitokage_ext/double-conversion/cached-powers.h +64 -0
  20. data/ext/hitokage_ext/double-conversion/diy-fp.cc +57 -0
  21. data/ext/hitokage_ext/double-conversion/diy-fp.h +118 -0
  22. data/ext/hitokage_ext/double-conversion/double-conversion.cc +982 -0
  23. data/ext/hitokage_ext/double-conversion/double-conversion.h +543 -0
  24. data/ext/hitokage_ext/double-conversion/fast-dtoa.cc +665 -0
  25. data/ext/hitokage_ext/double-conversion/fast-dtoa.h +88 -0
  26. data/ext/hitokage_ext/double-conversion/fixed-dtoa.cc +404 -0
  27. data/ext/hitokage_ext/double-conversion/fixed-dtoa.h +56 -0
  28. data/ext/hitokage_ext/double-conversion/ieee.h +402 -0
  29. data/ext/hitokage_ext/double-conversion/strtod.cc +555 -0
  30. data/ext/hitokage_ext/double-conversion/strtod.h +45 -0
  31. data/ext/hitokage_ext/double-conversion/utils.h +341 -0
  32. data/ext/hitokage_ext/extconf.rb +10 -0
  33. data/ext/hitokage_ext/hitokage_ext.cc +26 -0
  34. data/hitokage.gemspec +26 -0
  35. data/lib/hitokage.rb +5 -0
  36. data/lib/hitokage/replace_float_to_s.rb +6 -0
  37. data/lib/hitokage/version.rb +3 -0
  38. metadata +136 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2745cba975ccab0dc1c7c0d3ab657c5e658ba1fd
4
+ data.tar.gz: 0061376f8f16a7ececdee22bb067b81e55b9f582
5
+ SHA512:
6
+ metadata.gz: 2b3dd04ee315ed0cd5c20a028d10f43ba814d1dfef98c9201834bb58f605dd50ea35c295c5e5b9c1fbb0192e673068be15ad602cad08b709b319b00b7b8b3c88
7
+ data.tar.gz: deefe95ee48c9d90906cb7a646e7ad83ff3ee57bc7a0e1a16633e92ac582c52c1d0ab8b41fc50ca955ce897e379f17efa3aef65cb926e574f42dec4d1fb6a143
@@ -0,0 +1,20 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .sconsign.dblite
11
+ *.o
12
+ *.obj
13
+ msvc/Release/
14
+ msvc/Debug/
15
+ *.suo
16
+ *.opensdf
17
+ *.sdf
18
+ *.user
19
+ .DS_Store
20
+ lib/hitokage/hitokage_ext.bundle
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hitokage.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Soutaro Matsumoto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # Hitokage - Faster Float#to_s
2
+
3
+ The ruby's `Float#to_s` uses [David M. Gay's library](http://www.netlib.org/fp/dtoa.c).
4
+ The implementation is based on traditional DRAGON(1)(2), one of the most well known floating point printing algorithm developed by Guy L. Steel, et al.
5
+ I recently found another floating point printing library, [google/double-conversion](https://github.com/google/double-conversion), which implements faster algorithm called Grisu(3).
6
+
7
+ This is an example using the google/double-conversion for `Float#to_s`.
8
+
9
+ 1. Guy L. Steel, Jr and Jon L. White, How to Print Floating-Point Acculately, PLDI '90, http://dl.acm.org/ft_gateway.cfm?id=93559
10
+ 2. Robert G. Burger and R. Kent Dybvig, Printing Floating-Point Numbers Quickly and Acculately, PLDI '96, http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
11
+ 3. Florian Loitsch, Printing Floating-Point Numbers Quickly and Accurately with Integers, PLDI '10, http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
12
+
13
+ ### Benchmark
14
+
15
+ Grisu version is more than 4 times faster than DRAGON version, `Float#to_s`.
16
+
17
+ ```
18
+ $ ruby -I lib test/benchmark.rb
19
+ 🍉 Small float numbers, count=5000000, range=0.0...1.0
20
+ user system total real
21
+ Float#to_s 4.500000 0.010000 4.510000 ( 4.527218)
22
+ Float#hitokage_to_s 1.070000 0.000000 1.070000 ( 1.083553)
23
+ Verifying results...👍
24
+
25
+ 🍉 Usual float numbers, count=5000000, range=0.0...10000000.0
26
+ user system total real
27
+ Float#to_s 4.650000 0.330000 4.980000 ( 5.004367)
28
+ Float#hitokage_to_s 1.210000 0.140000 1.350000 ( 1.364027)
29
+ Verifying results...👍
30
+
31
+ 🍉 Big floats numbers, count=5000000, range=1.0e+20...1.0e+30
32
+ user system total real
33
+ Float#to_s 6.050000 0.240000 6.290000 ( 6.338893)
34
+ Float#hitokage_to_s 1.120000 0.000000 1.120000 ( 1.132833)
35
+ Verifying results...👍
36
+ ```
37
+
38
+ ## Installation
39
+
40
+ Add this line to your application's Gemfile:
41
+
42
+ ```ruby
43
+ gem 'hitokage'
44
+ ```
45
+
46
+ And then execute:
47
+
48
+ $ bundle
49
+
50
+ Or install it yourself as:
51
+
52
+ $ gem install hitokage
53
+
54
+ ## Usage
55
+
56
+ It defines `Float#hitokage_to_s`.
57
+
58
+ ```ruby
59
+ require 'hitokage'
60
+ 1.0.hitokage_to_s
61
+ ```
62
+
63
+ Or, you can replace the default `Float#to_s`.
64
+
65
+ ```ruby
66
+ require 'hitokage/replace_float_to_s'
67
+ 1.0.to_s
68
+ ```
69
+
70
+ ### Incompatibility
71
+
72
+ This library generates different string representation than `Float#to_s` for some values including `0.0`, `1e-10`, and `1e10`.
73
+ However, the generated string can be converted to same float number using `to_f`.
74
+
75
+ ## Development
76
+
77
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
78
+
79
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
80
+
81
+ ## Contributing
82
+
83
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hitokage.
84
+
85
+ ## License
86
+
87
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
88
+
89
+ The gem contains [google/double-conversion](https://github.com/google/double-conversion).
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rake/extensiontask"
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << "test"
7
+ t.libs << "lib"
8
+ t.test_files = FileList['test/**/*_test.rb']
9
+ end
10
+
11
+ Rake::ExtensionTask.new "hitokage_ext" do |ext|
12
+ ext.lib_dir = "lib/hitokage"
13
+ end
14
+
15
+ task :benchmark do |t|
16
+ sh "ruby -I lib test/benchmark.rb"
17
+ end
18
+
19
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hitokage"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,14 @@
1
+ # Below is a list of people and organizations that have contributed
2
+ # to the double-conversion project. Names should be added to the
3
+ # list like so:
4
+ #
5
+ # Name/Organization <email address>
6
+
7
+ Google Inc.
8
+ Mozilla Foundation
9
+
10
+ Jeff Muizelaar <jmuizelaar@mozilla.com>
11
+ Mike Hommey <mhommey@mozilla.com>
12
+ Martin Olsson <mnemo@minimum.se>
13
+ Kent Williams <chaircrusher@gmail.com>
14
+ Elan Ruusamäe <glen@delfi.ee>
@@ -0,0 +1,26 @@
1
+ Copyright 2006-2011, the V8 project authors. All rights reserved.
2
+ Redistribution and use in source and binary forms, with or without
3
+ modification, are permitted provided that the following conditions are
4
+ met:
5
+
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above
9
+ copyright notice, this list of conditions and the following
10
+ disclaimer in the documentation and/or other materials provided
11
+ with the distribution.
12
+ * Neither the name of Google Inc. nor the names of its
13
+ contributors may be used to endorse or promote products derived
14
+ from this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,26 @@
1
+ Copyright 2006-2011, the V8 project authors. All rights reserved.
2
+ Redistribution and use in source and binary forms, with or without
3
+ modification, are permitted provided that the following conditions are
4
+ met:
5
+
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above
9
+ copyright notice, this list of conditions and the following
10
+ disclaimer in the documentation and/or other materials provided
11
+ with the distribution.
12
+ * Neither the name of Google Inc. nor the names of its
13
+ contributors may be used to endorse or promote products derived
14
+ from this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,54 @@
1
+ https://github.com/google/double-conversion/
2
+
3
+ This project (double-conversion) provides binary-decimal and decimal-binary
4
+ routines for IEEE doubles.
5
+
6
+ The library consists of efficient conversion routines that have been extracted
7
+ from the V8 JavaScript engine. The code has been refactored and improved so that
8
+ it can be used more easily in other projects.
9
+
10
+ There is extensive documentation in double-conversion/double-conversion.h. Other
11
+ examples can be found in test/cctest/test-conversions.cc.
12
+
13
+
14
+ Building
15
+ ========
16
+
17
+ This library can be built with scons [0] or cmake [1].
18
+ The checked-in Makefile simply forwards to scons, and provides a
19
+ shortcut to run all tests:
20
+
21
+ make
22
+ make test
23
+
24
+ Scons
25
+ -----
26
+
27
+ The easiest way to install this library is to use `scons`. It builds
28
+ the static and shared library, and is set up to install those at the
29
+ correct locations:
30
+
31
+ scons install
32
+
33
+ Use the `DESTDIR` option to change the target directory:
34
+
35
+ scons DESTDIR=alternative_directory install
36
+
37
+ Cmake
38
+ -----
39
+
40
+ To use cmake run `cmake .` in the root directory. This overwrites the
41
+ existing Makefile.
42
+
43
+ Use `-DBUILD_SHARED_LIBS=ON` to enable the compilation of shared libraries.
44
+ Note that this disables static libraries. There is currently no way to
45
+ build both libraries at the same time with cmake.
46
+
47
+ Use `-DBUILD_TESTING=ON` to build the test executable.
48
+
49
+ cmake . -DBUILD_TESTING=ON
50
+ make
51
+ test/cctest/cctest --list | tr -d '<' | xargs test/cctest/cctest
52
+
53
+ [0]: http://www.scons.org
54
+ [1]: http://www.cmake.org
@@ -0,0 +1,641 @@
1
+ // Copyright 2010 the V8 project authors. All rights reserved.
2
+ // Redistribution and use in source and binary forms, with or without
3
+ // modification, are permitted provided that the following conditions are
4
+ // met:
5
+ //
6
+ // * Redistributions of source code must retain the above copyright
7
+ // notice, this list of conditions and the following disclaimer.
8
+ // * Redistributions in binary form must reproduce the above
9
+ // copyright notice, this list of conditions and the following
10
+ // disclaimer in the documentation and/or other materials provided
11
+ // with the distribution.
12
+ // * Neither the name of Google Inc. nor the names of its
13
+ // contributors may be used to endorse or promote products derived
14
+ // from this software without specific prior written permission.
15
+ //
16
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+ // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+ // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ #include <math.h>
29
+
30
+ #include "bignum-dtoa.h"
31
+
32
+ #include "bignum.h"
33
+ #include "ieee.h"
34
+
35
+ namespace double_conversion {
36
+
37
+ static int NormalizedExponent(uint64_t significand, int exponent) {
38
+ ASSERT(significand != 0);
39
+ while ((significand & Double::kHiddenBit) == 0) {
40
+ significand = significand << 1;
41
+ exponent = exponent - 1;
42
+ }
43
+ return exponent;
44
+ }
45
+
46
+
47
+ // Forward declarations:
48
+ // Returns an estimation of k such that 10^(k-1) <= v < 10^k.
49
+ static int EstimatePower(int exponent);
50
+ // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
51
+ // and denominator.
52
+ static void InitialScaledStartValues(uint64_t significand,
53
+ int exponent,
54
+ bool lower_boundary_is_closer,
55
+ int estimated_power,
56
+ bool need_boundary_deltas,
57
+ Bignum* numerator,
58
+ Bignum* denominator,
59
+ Bignum* delta_minus,
60
+ Bignum* delta_plus);
61
+ // Multiplies numerator/denominator so that its values lies in the range 1-10.
62
+ // Returns decimal_point s.t.
63
+ // v = numerator'/denominator' * 10^(decimal_point-1)
64
+ // where numerator' and denominator' are the values of numerator and
65
+ // denominator after the call to this function.
66
+ static void FixupMultiply10(int estimated_power, bool is_even,
67
+ int* decimal_point,
68
+ Bignum* numerator, Bignum* denominator,
69
+ Bignum* delta_minus, Bignum* delta_plus);
70
+ // Generates digits from the left to the right and stops when the generated
71
+ // digits yield the shortest decimal representation of v.
72
+ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
73
+ Bignum* delta_minus, Bignum* delta_plus,
74
+ bool is_even,
75
+ Vector<char> buffer, int* length);
76
+ // Generates 'requested_digits' after the decimal point.
77
+ static void BignumToFixed(int requested_digits, int* decimal_point,
78
+ Bignum* numerator, Bignum* denominator,
79
+ Vector<char>(buffer), int* length);
80
+ // Generates 'count' digits of numerator/denominator.
81
+ // Once 'count' digits have been produced rounds the result depending on the
82
+ // remainder (remainders of exactly .5 round upwards). Might update the
83
+ // decimal_point when rounding up (for example for 0.9999).
84
+ static void GenerateCountedDigits(int count, int* decimal_point,
85
+ Bignum* numerator, Bignum* denominator,
86
+ Vector<char>(buffer), int* length);
87
+
88
+
89
+ void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
90
+ Vector<char> buffer, int* length, int* decimal_point) {
91
+ ASSERT(v > 0);
92
+ ASSERT(!Double(v).IsSpecial());
93
+ uint64_t significand;
94
+ int exponent;
95
+ bool lower_boundary_is_closer;
96
+ if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) {
97
+ float f = static_cast<float>(v);
98
+ ASSERT(f == v);
99
+ significand = Single(f).Significand();
100
+ exponent = Single(f).Exponent();
101
+ lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser();
102
+ } else {
103
+ significand = Double(v).Significand();
104
+ exponent = Double(v).Exponent();
105
+ lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser();
106
+ }
107
+ bool need_boundary_deltas =
108
+ (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE);
109
+
110
+ bool is_even = (significand & 1) == 0;
111
+ int normalized_exponent = NormalizedExponent(significand, exponent);
112
+ // estimated_power might be too low by 1.
113
+ int estimated_power = EstimatePower(normalized_exponent);
114
+
115
+ // Shortcut for Fixed.
116
+ // The requested digits correspond to the digits after the point. If the
117
+ // number is much too small, then there is no need in trying to get any
118
+ // digits.
119
+ if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
120
+ buffer[0] = '\0';
121
+ *length = 0;
122
+ // Set decimal-point to -requested_digits. This is what Gay does.
123
+ // Note that it should not have any effect anyways since the string is
124
+ // empty.
125
+ *decimal_point = -requested_digits;
126
+ return;
127
+ }
128
+
129
+ Bignum numerator;
130
+ Bignum denominator;
131
+ Bignum delta_minus;
132
+ Bignum delta_plus;
133
+ // Make sure the bignum can grow large enough. The smallest double equals
134
+ // 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
135
+ // The maximum double is 1.7976931348623157e308 which needs fewer than
136
+ // 308*4 binary digits.
137
+ ASSERT(Bignum::kMaxSignificantBits >= 324*4);
138
+ InitialScaledStartValues(significand, exponent, lower_boundary_is_closer,
139
+ estimated_power, need_boundary_deltas,
140
+ &numerator, &denominator,
141
+ &delta_minus, &delta_plus);
142
+ // We now have v = (numerator / denominator) * 10^estimated_power.
143
+ FixupMultiply10(estimated_power, is_even, decimal_point,
144
+ &numerator, &denominator,
145
+ &delta_minus, &delta_plus);
146
+ // We now have v = (numerator / denominator) * 10^(decimal_point-1), and
147
+ // 1 <= (numerator + delta_plus) / denominator < 10
148
+ switch (mode) {
149
+ case BIGNUM_DTOA_SHORTEST:
150
+ case BIGNUM_DTOA_SHORTEST_SINGLE:
151
+ GenerateShortestDigits(&numerator, &denominator,
152
+ &delta_minus, &delta_plus,
153
+ is_even, buffer, length);
154
+ break;
155
+ case BIGNUM_DTOA_FIXED:
156
+ BignumToFixed(requested_digits, decimal_point,
157
+ &numerator, &denominator,
158
+ buffer, length);
159
+ break;
160
+ case BIGNUM_DTOA_PRECISION:
161
+ GenerateCountedDigits(requested_digits, decimal_point,
162
+ &numerator, &denominator,
163
+ buffer, length);
164
+ break;
165
+ default:
166
+ UNREACHABLE();
167
+ }
168
+ buffer[*length] = '\0';
169
+ }
170
+
171
+
172
+ // The procedure starts generating digits from the left to the right and stops
173
+ // when the generated digits yield the shortest decimal representation of v. A
174
+ // decimal representation of v is a number lying closer to v than to any other
175
+ // double, so it converts to v when read.
176
+ //
177
+ // This is true if d, the decimal representation, is between m- and m+, the
178
+ // upper and lower boundaries. d must be strictly between them if !is_even.
179
+ // m- := (numerator - delta_minus) / denominator
180
+ // m+ := (numerator + delta_plus) / denominator
181
+ //
182
+ // Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
183
+ // If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
184
+ // will be produced. This should be the standard precondition.
185
+ static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
186
+ Bignum* delta_minus, Bignum* delta_plus,
187
+ bool is_even,
188
+ Vector<char> buffer, int* length) {
189
+ // Small optimization: if delta_minus and delta_plus are the same just reuse
190
+ // one of the two bignums.
191
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
192
+ delta_plus = delta_minus;
193
+ }
194
+ *length = 0;
195
+ for (;;) {
196
+ uint16_t digit;
197
+ digit = numerator->DivideModuloIntBignum(*denominator);
198
+ ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
199
+ // digit = numerator / denominator (integer division).
200
+ // numerator = numerator % denominator.
201
+ buffer[(*length)++] = static_cast<char>(digit + '0');
202
+
203
+ // Can we stop already?
204
+ // If the remainder of the division is less than the distance to the lower
205
+ // boundary we can stop. In this case we simply round down (discarding the
206
+ // remainder).
207
+ // Similarly we test if we can round up (using the upper boundary).
208
+ bool in_delta_room_minus;
209
+ bool in_delta_room_plus;
210
+ if (is_even) {
211
+ in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
212
+ } else {
213
+ in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
214
+ }
215
+ if (is_even) {
216
+ in_delta_room_plus =
217
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
218
+ } else {
219
+ in_delta_room_plus =
220
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
221
+ }
222
+ if (!in_delta_room_minus && !in_delta_room_plus) {
223
+ // Prepare for next iteration.
224
+ numerator->Times10();
225
+ delta_minus->Times10();
226
+ // We optimized delta_plus to be equal to delta_minus (if they share the
227
+ // same value). So don't multiply delta_plus if they point to the same
228
+ // object.
229
+ if (delta_minus != delta_plus) {
230
+ delta_plus->Times10();
231
+ }
232
+ } else if (in_delta_room_minus && in_delta_room_plus) {
233
+ // Let's see if 2*numerator < denominator.
234
+ // If yes, then the next digit would be < 5 and we can round down.
235
+ int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
236
+ if (compare < 0) {
237
+ // Remaining digits are less than .5. -> Round down (== do nothing).
238
+ } else if (compare > 0) {
239
+ // Remaining digits are more than .5 of denominator. -> Round up.
240
+ // Note that the last digit could not be a '9' as otherwise the whole
241
+ // loop would have stopped earlier.
242
+ // We still have an assert here in case the preconditions were not
243
+ // satisfied.
244
+ ASSERT(buffer[(*length) - 1] != '9');
245
+ buffer[(*length) - 1]++;
246
+ } else {
247
+ // Halfway case.
248
+ // TODO(floitsch): need a way to solve half-way cases.
249
+ // For now let's round towards even (since this is what Gay seems to
250
+ // do).
251
+
252
+ if ((buffer[(*length) - 1] - '0') % 2 == 0) {
253
+ // Round down => Do nothing.
254
+ } else {
255
+ ASSERT(buffer[(*length) - 1] != '9');
256
+ buffer[(*length) - 1]++;
257
+ }
258
+ }
259
+ return;
260
+ } else if (in_delta_room_minus) {
261
+ // Round down (== do nothing).
262
+ return;
263
+ } else { // in_delta_room_plus
264
+ // Round up.
265
+ // Note again that the last digit could not be '9' since this would have
266
+ // stopped the loop earlier.
267
+ // We still have an ASSERT here, in case the preconditions were not
268
+ // satisfied.
269
+ ASSERT(buffer[(*length) -1] != '9');
270
+ buffer[(*length) - 1]++;
271
+ return;
272
+ }
273
+ }
274
+ }
275
+
276
+
277
+ // Let v = numerator / denominator < 10.
278
+ // Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
279
+ // from left to right. Once 'count' digits have been produced we decide wether
280
+ // to round up or down. Remainders of exactly .5 round upwards. Numbers such
281
+ // as 9.999999 propagate a carry all the way, and change the
282
+ // exponent (decimal_point), when rounding upwards.
283
+ static void GenerateCountedDigits(int count, int* decimal_point,
284
+ Bignum* numerator, Bignum* denominator,
285
+ Vector<char> buffer, int* length) {
286
+ ASSERT(count >= 0);
287
+ for (int i = 0; i < count - 1; ++i) {
288
+ uint16_t digit;
289
+ digit = numerator->DivideModuloIntBignum(*denominator);
290
+ ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
291
+ // digit = numerator / denominator (integer division).
292
+ // numerator = numerator % denominator.
293
+ buffer[i] = static_cast<char>(digit + '0');
294
+ // Prepare for next iteration.
295
+ numerator->Times10();
296
+ }
297
+ // Generate the last digit.
298
+ uint16_t digit;
299
+ digit = numerator->DivideModuloIntBignum(*denominator);
300
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
301
+ digit++;
302
+ }
303
+ ASSERT(digit <= 10);
304
+ buffer[count - 1] = static_cast<char>(digit + '0');
305
+ // Correct bad digits (in case we had a sequence of '9's). Propagate the
306
+ // carry until we hat a non-'9' or til we reach the first digit.
307
+ for (int i = count - 1; i > 0; --i) {
308
+ if (buffer[i] != '0' + 10) break;
309
+ buffer[i] = '0';
310
+ buffer[i - 1]++;
311
+ }
312
+ if (buffer[0] == '0' + 10) {
313
+ // Propagate a carry past the top place.
314
+ buffer[0] = '1';
315
+ (*decimal_point)++;
316
+ }
317
+ *length = count;
318
+ }
319
+
320
+
321
+ // Generates 'requested_digits' after the decimal point. It might omit
322
+ // trailing '0's. If the input number is too small then no digits at all are
323
+ // generated (ex.: 2 fixed digits for 0.00001).
324
+ //
325
+ // Input verifies: 1 <= (numerator + delta) / denominator < 10.
326
+ static void BignumToFixed(int requested_digits, int* decimal_point,
327
+ Bignum* numerator, Bignum* denominator,
328
+ Vector<char>(buffer), int* length) {
329
+ // Note that we have to look at more than just the requested_digits, since
330
+ // a number could be rounded up. Example: v=0.5 with requested_digits=0.
331
+ // Even though the power of v equals 0 we can't just stop here.
332
+ if (-(*decimal_point) > requested_digits) {
333
+ // The number is definitively too small.
334
+ // Ex: 0.001 with requested_digits == 1.
335
+ // Set decimal-point to -requested_digits. This is what Gay does.
336
+ // Note that it should not have any effect anyways since the string is
337
+ // empty.
338
+ *decimal_point = -requested_digits;
339
+ *length = 0;
340
+ return;
341
+ } else if (-(*decimal_point) == requested_digits) {
342
+ // We only need to verify if the number rounds down or up.
343
+ // Ex: 0.04 and 0.06 with requested_digits == 1.
344
+ ASSERT(*decimal_point == -requested_digits);
345
+ // Initially the fraction lies in range (1, 10]. Multiply the denominator
346
+ // by 10 so that we can compare more easily.
347
+ denominator->Times10();
348
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
349
+ // If the fraction is >= 0.5 then we have to include the rounded
350
+ // digit.
351
+ buffer[0] = '1';
352
+ *length = 1;
353
+ (*decimal_point)++;
354
+ } else {
355
+ // Note that we caught most of similar cases earlier.
356
+ *length = 0;
357
+ }
358
+ return;
359
+ } else {
360
+ // The requested digits correspond to the digits after the point.
361
+ // The variable 'needed_digits' includes the digits before the point.
362
+ int needed_digits = (*decimal_point) + requested_digits;
363
+ GenerateCountedDigits(needed_digits, decimal_point,
364
+ numerator, denominator,
365
+ buffer, length);
366
+ }
367
+ }
368
+
369
+
370
+ // Returns an estimation of k such that 10^(k-1) <= v < 10^k where
371
+ // v = f * 2^exponent and 2^52 <= f < 2^53.
372
+ // v is hence a normalized double with the given exponent. The output is an
373
+ // approximation for the exponent of the decimal approimation .digits * 10^k.
374
+ //
375
+ // The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
376
+ // Note: this property holds for v's upper boundary m+ too.
377
+ // 10^k <= m+ < 10^k+1.
378
+ // (see explanation below).
379
+ //
380
+ // Examples:
381
+ // EstimatePower(0) => 16
382
+ // EstimatePower(-52) => 0
383
+ //
384
+ // Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
385
+ static int EstimatePower(int exponent) {
386
+ // This function estimates log10 of v where v = f*2^e (with e == exponent).
387
+ // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
388
+ // Note that f is bounded by its container size. Let p = 53 (the double's
389
+ // significand size). Then 2^(p-1) <= f < 2^p.
390
+ //
391
+ // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
392
+ // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
393
+ // The computed number undershoots by less than 0.631 (when we compute log3
394
+ // and not log10).
395
+ //
396
+ // Optimization: since we only need an approximated result this computation
397
+ // can be performed on 64 bit integers. On x86/x64 architecture the speedup is
398
+ // not really measurable, though.
399
+ //
400
+ // Since we want to avoid overshooting we decrement by 1e10 so that
401
+ // floating-point imprecisions don't affect us.
402
+ //
403
+ // Explanation for v's boundary m+: the computation takes advantage of
404
+ // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
405
+ // (even for denormals where the delta can be much more important).
406
+
407
+ const double k1Log10 = 0.30102999566398114; // 1/lg(10)
408
+
409
+ // For doubles len(f) == 53 (don't forget the hidden bit).
410
+ const int kSignificandSize = Double::kSignificandSize;
411
+ double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
412
+ return static_cast<int>(estimate);
413
+ }
414
+
415
+
416
+ // See comments for InitialScaledStartValues.
417
+ static void InitialScaledStartValuesPositiveExponent(
418
+ uint64_t significand, int exponent,
419
+ int estimated_power, bool need_boundary_deltas,
420
+ Bignum* numerator, Bignum* denominator,
421
+ Bignum* delta_minus, Bignum* delta_plus) {
422
+ // A positive exponent implies a positive power.
423
+ ASSERT(estimated_power >= 0);
424
+ // Since the estimated_power is positive we simply multiply the denominator
425
+ // by 10^estimated_power.
426
+
427
+ // numerator = v.
428
+ numerator->AssignUInt64(significand);
429
+ numerator->ShiftLeft(exponent);
430
+ // denominator = 10^estimated_power.
431
+ denominator->AssignPowerUInt16(10, estimated_power);
432
+
433
+ if (need_boundary_deltas) {
434
+ // Introduce a common denominator so that the deltas to the boundaries are
435
+ // integers.
436
+ denominator->ShiftLeft(1);
437
+ numerator->ShiftLeft(1);
438
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
439
+ // denominator (of 2) delta_plus equals 2^e.
440
+ delta_plus->AssignUInt16(1);
441
+ delta_plus->ShiftLeft(exponent);
442
+ // Same for delta_minus. The adjustments if f == 2^p-1 are done later.
443
+ delta_minus->AssignUInt16(1);
444
+ delta_minus->ShiftLeft(exponent);
445
+ }
446
+ }
447
+
448
+
449
+ // See comments for InitialScaledStartValues
450
+ static void InitialScaledStartValuesNegativeExponentPositivePower(
451
+ uint64_t significand, int exponent,
452
+ int estimated_power, bool need_boundary_deltas,
453
+ Bignum* numerator, Bignum* denominator,
454
+ Bignum* delta_minus, Bignum* delta_plus) {
455
+ // v = f * 2^e with e < 0, and with estimated_power >= 0.
456
+ // This means that e is close to 0 (have a look at how estimated_power is
457
+ // computed).
458
+
459
+ // numerator = significand
460
+ // since v = significand * 2^exponent this is equivalent to
461
+ // numerator = v * / 2^-exponent
462
+ numerator->AssignUInt64(significand);
463
+ // denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
464
+ denominator->AssignPowerUInt16(10, estimated_power);
465
+ denominator->ShiftLeft(-exponent);
466
+
467
+ if (need_boundary_deltas) {
468
+ // Introduce a common denominator so that the deltas to the boundaries are
469
+ // integers.
470
+ denominator->ShiftLeft(1);
471
+ numerator->ShiftLeft(1);
472
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
473
+ // denominator (of 2) delta_plus equals 2^e.
474
+ // Given that the denominator already includes v's exponent the distance
475
+ // to the boundaries is simply 1.
476
+ delta_plus->AssignUInt16(1);
477
+ // Same for delta_minus. The adjustments if f == 2^p-1 are done later.
478
+ delta_minus->AssignUInt16(1);
479
+ }
480
+ }
481
+
482
+
483
+ // See comments for InitialScaledStartValues
484
+ static void InitialScaledStartValuesNegativeExponentNegativePower(
485
+ uint64_t significand, int exponent,
486
+ int estimated_power, bool need_boundary_deltas,
487
+ Bignum* numerator, Bignum* denominator,
488
+ Bignum* delta_minus, Bignum* delta_plus) {
489
+ // Instead of multiplying the denominator with 10^estimated_power we
490
+ // multiply all values (numerator and deltas) by 10^-estimated_power.
491
+
492
+ // Use numerator as temporary container for power_ten.
493
+ Bignum* power_ten = numerator;
494
+ power_ten->AssignPowerUInt16(10, -estimated_power);
495
+
496
+ if (need_boundary_deltas) {
497
+ // Since power_ten == numerator we must make a copy of 10^estimated_power
498
+ // before we complete the computation of the numerator.
499
+ // delta_plus = delta_minus = 10^estimated_power
500
+ delta_plus->AssignBignum(*power_ten);
501
+ delta_minus->AssignBignum(*power_ten);
502
+ }
503
+
504
+ // numerator = significand * 2 * 10^-estimated_power
505
+ // since v = significand * 2^exponent this is equivalent to
506
+ // numerator = v * 10^-estimated_power * 2 * 2^-exponent.
507
+ // Remember: numerator has been abused as power_ten. So no need to assign it
508
+ // to itself.
509
+ ASSERT(numerator == power_ten);
510
+ numerator->MultiplyByUInt64(significand);
511
+
512
+ // denominator = 2 * 2^-exponent with exponent < 0.
513
+ denominator->AssignUInt16(1);
514
+ denominator->ShiftLeft(-exponent);
515
+
516
+ if (need_boundary_deltas) {
517
+ // Introduce a common denominator so that the deltas to the boundaries are
518
+ // integers.
519
+ numerator->ShiftLeft(1);
520
+ denominator->ShiftLeft(1);
521
+ // With this shift the boundaries have their correct value, since
522
+ // delta_plus = 10^-estimated_power, and
523
+ // delta_minus = 10^-estimated_power.
524
+ // These assignments have been done earlier.
525
+ // The adjustments if f == 2^p-1 (lower boundary is closer) are done later.
526
+ }
527
+ }
528
+
529
+
530
+ // Let v = significand * 2^exponent.
531
+ // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
532
+ // and denominator. The functions GenerateShortestDigits and
533
+ // GenerateCountedDigits will then convert this ratio to its decimal
534
+ // representation d, with the required accuracy.
535
+ // Then d * 10^estimated_power is the representation of v.
536
+ // (Note: the fraction and the estimated_power might get adjusted before
537
+ // generating the decimal representation.)
538
+ //
539
+ // The initial start values consist of:
540
+ // - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
541
+ // - a scaled (common) denominator.
542
+ // optionally (used by GenerateShortestDigits to decide if it has the shortest
543
+ // decimal converting back to v):
544
+ // - v - m-: the distance to the lower boundary.
545
+ // - m+ - v: the distance to the upper boundary.
546
+ //
547
+ // v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
548
+ //
549
+ // Let ep == estimated_power, then the returned values will satisfy:
550
+ // v / 10^ep = numerator / denominator.
551
+ // v's boundarys m- and m+:
552
+ // m- / 10^ep == v / 10^ep - delta_minus / denominator
553
+ // m+ / 10^ep == v / 10^ep + delta_plus / denominator
554
+ // Or in other words:
555
+ // m- == v - delta_minus * 10^ep / denominator;
556
+ // m+ == v + delta_plus * 10^ep / denominator;
557
+ //
558
+ // Since 10^(k-1) <= v < 10^k (with k == estimated_power)
559
+ // or 10^k <= v < 10^(k+1)
560
+ // we then have 0.1 <= numerator/denominator < 1
561
+ // or 1 <= numerator/denominator < 10
562
+ //
563
+ // It is then easy to kickstart the digit-generation routine.
564
+ //
565
+ // The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST
566
+ // or BIGNUM_DTOA_SHORTEST_SINGLE.
567
+
568
+ static void InitialScaledStartValues(uint64_t significand,
569
+ int exponent,
570
+ bool lower_boundary_is_closer,
571
+ int estimated_power,
572
+ bool need_boundary_deltas,
573
+ Bignum* numerator,
574
+ Bignum* denominator,
575
+ Bignum* delta_minus,
576
+ Bignum* delta_plus) {
577
+ if (exponent >= 0) {
578
+ InitialScaledStartValuesPositiveExponent(
579
+ significand, exponent, estimated_power, need_boundary_deltas,
580
+ numerator, denominator, delta_minus, delta_plus);
581
+ } else if (estimated_power >= 0) {
582
+ InitialScaledStartValuesNegativeExponentPositivePower(
583
+ significand, exponent, estimated_power, need_boundary_deltas,
584
+ numerator, denominator, delta_minus, delta_plus);
585
+ } else {
586
+ InitialScaledStartValuesNegativeExponentNegativePower(
587
+ significand, exponent, estimated_power, need_boundary_deltas,
588
+ numerator, denominator, delta_minus, delta_plus);
589
+ }
590
+
591
+ if (need_boundary_deltas && lower_boundary_is_closer) {
592
+ // The lower boundary is closer at half the distance of "normal" numbers.
593
+ // Increase the common denominator and adapt all but the delta_minus.
594
+ denominator->ShiftLeft(1); // *2
595
+ numerator->ShiftLeft(1); // *2
596
+ delta_plus->ShiftLeft(1); // *2
597
+ }
598
+ }
599
+
600
+
601
+ // This routine multiplies numerator/denominator so that its values lies in the
602
+ // range 1-10. That is after a call to this function we have:
603
+ // 1 <= (numerator + delta_plus) /denominator < 10.
604
+ // Let numerator the input before modification and numerator' the argument
605
+ // after modification, then the output-parameter decimal_point is such that
606
+ // numerator / denominator * 10^estimated_power ==
607
+ // numerator' / denominator' * 10^(decimal_point - 1)
608
+ // In some cases estimated_power was too low, and this is already the case. We
609
+ // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
610
+ // estimated_power) but do not touch the numerator or denominator.
611
+ // Otherwise the routine multiplies the numerator and the deltas by 10.
612
+ static void FixupMultiply10(int estimated_power, bool is_even,
613
+ int* decimal_point,
614
+ Bignum* numerator, Bignum* denominator,
615
+ Bignum* delta_minus, Bignum* delta_plus) {
616
+ bool in_range;
617
+ if (is_even) {
618
+ // For IEEE doubles half-way cases (in decimal system numbers ending with 5)
619
+ // are rounded to the closest floating-point number with even significand.
620
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
621
+ } else {
622
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
623
+ }
624
+ if (in_range) {
625
+ // Since numerator + delta_plus >= denominator we already have
626
+ // 1 <= numerator/denominator < 10. Simply update the estimated_power.
627
+ *decimal_point = estimated_power + 1;
628
+ } else {
629
+ *decimal_point = estimated_power;
630
+ numerator->Times10();
631
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
632
+ delta_minus->Times10();
633
+ delta_plus->AssignBignum(*delta_minus);
634
+ } else {
635
+ delta_minus->Times10();
636
+ delta_plus->Times10();
637
+ }
638
+ }
639
+ }
640
+
641
+ } // namespace double_conversion