ae_fast_decimal_formatter 0.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8d0765a81889d9e8cb54d9961fd5bc6e7daf282e767bc10f33fb5c6660df418
4
- data.tar.gz: 8f0064baa8132ac69a131d3c45a3917d1288672815a7bd280ff5c718e9e62b05
3
+ metadata.gz: bfad6f29472a1ffc004d85c66182f379905f04d968324acba49ded7c3bf277c9
4
+ data.tar.gz: b351216f297f3c692f42fd44e0ba917a3405e677c567fa9206d6d8f4cba936c3
5
5
  SHA512:
6
- metadata.gz: ce7e446c20366f9b69bdf58285d6b960957ecbd96f88ca427d6d3e9e9c80cb5b0fa325b21f726544bb33b888c49aef309d16db76d1bee44a4ab357efbb757a0e
7
- data.tar.gz: 55fe31c15e4a985a36cb6580685a82043407761c4c086f2da809ad6ff421a0cee776555ba712ac0190577e2abd8e8d2a08dcd017e87687095c1ddeb0651c6fb4
6
+ metadata.gz: d3aa9e8b0438d4f4f0dd59f286685f4d2e0954ca6b50f6aed8a02c6e819e9be2abc7886468931e2e9cf2ea0bf70525decd5364468b99b30944dc74e2e40e08b2
7
+ data.tar.gz: 053b1593c4b4ed4ab05cbc1cc481361a65d9dac6b05ae1ec5f03ec06c931f712ababb7665e7cfefe6d129e2d4a9a3d7717772ca9b7502a28bed1db5e044f3946
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019-2022 AppFolio, Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/ae_fast_decimal_formatter/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'ae_fast_decimal_formatter'
7
+ spec.version = AeFastDecimalFormatter::VERSION
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.author = 'AppFolio'
10
+ spec.email = 'opensource@appfolio.com'
11
+ spec.description = 'Efficiently format decimal number.'
12
+ spec.summary = spec.description
13
+ spec.homepage = 'https://github.com/appfolio/ae_fast_decimal_formatter'
14
+ spec.license = 'MIT'
15
+ spec.files = Dir['**/*'].select { |f| f[%r{^(lib/|ext/|LICENSE.txt|.*gemspec)}] }
16
+ spec.require_paths = ['lib']
17
+ spec.extensions = ['ext/ae_fast_decimal_formatter/extconf.rb']
18
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.6.3')
19
+
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+
22
+ spec.add_dependency('addressable', ['>= 2.3.6', '< 3'])
23
+ spec.add_dependency('rack', ['>= 2.2.3', '< 3'])
24
+ end
@@ -1,106 +1,59 @@
1
1
  #include <ruby.h>
2
2
  #include <float.h>
3
3
 
4
- VALUE c_format_decimal(double num, int precision)
5
- {
6
- char numstr[100];
7
- char str[DBL_MAX_10_EXP + 2];
8
- int numi, numlen, stri, strl;
9
- int maxp = 5, i;
10
- char formatstr[10];
11
- int digits_to_comma;
12
- double rounding_factor, rounded_num;
4
+ inline static char * long_to_formatted_string(long value, char *str, unsigned int strlen, unsigned int precision) {
5
+ char *p;
6
+ unsigned long v;
13
7
 
14
- if (precision > maxp) { precision = maxp; }
15
- if (precision < 0) { precision = 0; }
8
+ v = (value < 0) ? -value : value;
9
+ p = str + strlen - 1;
16
10
 
17
- snprintf(formatstr, 10, "%%.%df", precision);
18
- formatstr[9] = 0;
19
-
20
- if (precision > 0) {
21
- rounding_factor = pow(10.0, precision);
22
- rounded_num = round(num * rounding_factor) / rounding_factor;
23
- } else {
24
- rounded_num = round(num);
25
- }
26
-
27
- if (precision > 0) { precision++; } // adjust for .
28
-
29
- numlen = snprintf(numstr, 100, formatstr, rounded_num);
30
- digits_to_comma = numlen - precision;
31
- if (numstr[0] == '-') { digits_to_comma--; }
32
-
33
- strl = numlen + (digits_to_comma / 3);
34
- if ((digits_to_comma % 3) == 0) {
35
- strl--;
36
- }
37
-
38
- if (strncmp(numstr, "-0.00000", strl) == 0) {
39
- return rb_str_new("0.00000", 1 + precision);
11
+ // Copy all the precision digits
12
+ for (unsigned int i = 0; i < precision; i++) {
13
+ *p-- = '0' + (v % 10);
14
+ v /= 10;
40
15
  }
41
16
 
42
- stri = strl;
43
- str[stri] = 0;
44
- for (i = 1; i <= (precision + 1); i++) {
45
- str[stri - i] = numstr[numlen - i];
17
+ // Add the dot
18
+ if (precision > 0) {
19
+ *p-- = '.';
46
20
  }
47
21
 
48
- stri -= (precision + 2);
49
- numlen -= (precision + 1);
50
- numi = 1;
22
+ // Copy all the digits, adding a comma every 3rd digit
23
+ int digits = 0;
24
+ do {
25
+ *p-- = '0' + (v % 10);
26
+ v /= 10;
51
27
 
52
- while ((numlen - numi) >= 1) {
53
- if ((numi % 3) == 0) {
54
- str[stri] = ',';
55
- stri--;
28
+ if ((++digits % 3) == 0 && v) {
29
+ *p-- = ',';
56
30
  }
57
- str[stri] = numstr[numlen - numi];
58
- stri--;
59
- numi++;
60
- }
31
+ } while(v);
61
32
 
62
- if (numstr[0] == '-') {
63
- str[0] = '-';
64
- } else {
65
- if ((numi % 3) == 0) {
66
- str[1] = ',';
67
- }
68
- str[0] = numstr[0];
69
- }
70
- return rb_str_new2(str);
71
- }
72
-
73
- struct ae_fast_decimal_formatter {
74
- double num;
75
- int precision;
76
- };
77
-
78
- static void ae_fast_decimal_formatter_free(void *p) {}
33
+ // Finally, add the - if we started with a negative number
34
+ if (value < 0) *p-- = '-';
79
35
 
80
- static VALUE ae_fast_decimal_formatter_alloc(VALUE klass) {
81
- VALUE obj;
82
- struct ae_fast_decimal_formatter *ptr;
83
-
84
- obj = Data_Make_Struct(klass, struct ae_fast_decimal_formatter, NULL, ae_fast_decimal_formatter_free, ptr);
85
-
86
- ptr->num = 0.0;
87
- ptr->precision = 0;
88
-
89
- return obj;
90
- }
36
+ // And adjust the p to undo the last p--
37
+ p++;
91
38
 
92
- static VALUE ae_fast_decimal_formatter_init(VALUE self, VALUE number, VALUE precision) {
93
- struct ae_fast_decimal_formatter *ptr;
94
- Data_Get_Struct(self, struct ae_fast_decimal_formatter, ptr);
95
- ptr->num = NUM2DBL(number);
96
- ptr->precision = NUM2UINT(precision);
97
- return self;
39
+ return p;
98
40
  }
99
41
 
100
- static VALUE ae_fast_decimal_formatter_format(VALUE self) {
101
- struct ae_fast_decimal_formatter *ptr;
102
- Data_Get_Struct(self, struct ae_fast_decimal_formatter, ptr);
103
- return c_format_decimal(ptr->num, ptr->precision);
42
+ // The function takes a value num (assumed to be fixnum) and value precision (assumed to be fixnum
43
+ // between 0 and 5) and returns num as a formatted string.
44
+ //
45
+ // For example, given 100, 2 the function will output "1.00".
46
+ // For example, given 123456, 2 the function will output "1,234.56".
47
+ // For example, given -1234, 0 the function will output "-1,234".
48
+ static VALUE ae_fast_decimal_formatter_format_long(VALUE self, VALUE num, VALUE precision) {
49
+ // The maximum length is 28 characters: 20 digits (assuming 64bits), 6 commas,
50
+ // negative sign, and a period. So we will always fit in 32 chars.
51
+ char buf[32];
52
+ char *numstr = long_to_formatted_string(NUM2LONG(num), buf, 32, NUM2UINT(precision));
53
+
54
+ // The rb_str_new takes a pointer to a string and a length. It does not rely on
55
+ // null terminated strings.
56
+ return rb_str_new(numstr, 32 - (int)(numstr - buf));
104
57
  }
105
58
 
106
59
  void Init_ae_fast_decimal_formatter(void) {
@@ -108,7 +61,5 @@ void Init_ae_fast_decimal_formatter(void) {
108
61
 
109
62
  cFastDecimalFormatter = rb_const_get(rb_cObject, rb_intern("AeFastDecimalFormatter"));
110
63
 
111
- rb_define_alloc_func(cFastDecimalFormatter, ae_fast_decimal_formatter_alloc);
112
- rb_define_method(cFastDecimalFormatter, "initialize", ae_fast_decimal_formatter_init, 2);
113
- rb_define_method(cFastDecimalFormatter, "format", ae_fast_decimal_formatter_format, 0);
64
+ rb_define_singleton_method(cFastDecimalFormatter, "format_long", ae_fast_decimal_formatter_format_long, 2);
114
65
  }
@@ -1,6 +1,3 @@
1
1
  require "mkmf"
2
2
 
3
- abort "missing snprintf()" unless have_func "snprintf"
4
- abort "missing strncmp()" unless have_func "strncmp"
5
-
6
3
  create_makefile "ae_fast_decimal_formatter/ae_fast_decimal_formatter"
@@ -0,0 +1,3 @@
1
+ class AeFastDecimalFormatter
2
+ VERSION = '2.0.0'
3
+ end
@@ -1,4 +1,23 @@
1
+ require 'bigdecimal'
2
+
1
3
  class AeFastDecimalFormatter
4
+ def self.format(number, precision)
5
+ precision = 0 if precision < 0
6
+ precision = 5 if precision > 5
7
+
8
+ if number.is_a?(BigDecimal)
9
+ # With BigDecimal, we do not need to call round twice.
10
+ format_long((number * 10 ** precision).round, precision)
11
+ else
12
+ # It looks like we need the two sets of rounds in order to attempt
13
+ # to round floating numbers. For example,
14
+ # (148.855 * 100).round -> 14885 seems incorrect, so we try,
15
+ # 148.855.round(2) * 100 -> 14886 but then,
16
+ # (1234567890.12.round(2) * 100).to_i -> 123456789011 which is
17
+ # definitely not correct. Thus the two rounds.
18
+ format_long((number.round(precision) * 10 ** precision).round, precision)
19
+ end
20
+ end
2
21
  end
3
22
 
4
23
  require "ae_fast_decimal_formatter/ae_fast_decimal_formatter"
metadata CHANGED
@@ -1,32 +1,75 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ae_fast_decimal_formatter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Appfolio Inc.
8
- autorequire:
7
+ - AppFolio
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-14 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Efficiently format decimal number so that reports of millions of decimal
14
- cells are fast
15
- email: dev@appfolio.com
11
+ date: 2022-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.6
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 2.3.6
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rack
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 2.2.3
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '3'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 2.2.3
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '3'
53
+ description: Efficiently format decimal number.
54
+ email: opensource@appfolio.com
16
55
  executables: []
17
56
  extensions:
18
57
  - ext/ae_fast_decimal_formatter/extconf.rb
19
58
  extra_rdoc_files: []
20
59
  files:
60
+ - LICENSE.txt
61
+ - ae_fast_decimal_formatter.gemspec
21
62
  - ext/ae_fast_decimal_formatter/ae_fast_decimal_formatter.c
22
63
  - ext/ae_fast_decimal_formatter/extconf.rb
23
64
  - lib/ae_fast_decimal_formatter.rb
24
- homepage: https://www.github.com/appfolio/ae_fast_decimal_formatter
65
+ - lib/ae_fast_decimal_formatter/ae_fast_decimal_formatter.bundle
66
+ - lib/ae_fast_decimal_formatter/version.rb
67
+ homepage: https://github.com/appfolio/ae_fast_decimal_formatter
25
68
  licenses:
26
69
  - MIT
27
70
  metadata:
28
71
  allowed_push_host: https://rubygems.org
29
- post_install_message:
72
+ post_install_message:
30
73
  rdoc_options: []
31
74
  require_paths:
32
75
  - lib
@@ -34,18 +77,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
34
77
  requirements:
35
78
  - - ">="
36
79
  - !ruby/object:Gem::Version
37
- version: 2.3.3
38
- - - "<"
39
- - !ruby/object:Gem::Version
40
- version: '2.7'
80
+ version: 2.6.3
41
81
  required_rubygems_version: !ruby/object:Gem::Requirement
42
82
  requirements:
43
83
  - - ">="
44
84
  - !ruby/object:Gem::Version
45
85
  version: '0'
46
86
  requirements: []
47
- rubygems_version: 3.0.3
48
- signing_key:
87
+ rubygems_version: 3.1.6
88
+ signing_key:
49
89
  specification_version: 4
50
- summary: Efficiently format decimal number
90
+ summary: Efficiently format decimal number.
51
91
  test_files: []