ae_fast_decimal_formatter 1.0.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: 45ca777593c0bfaf6e4cac01b3681d91935a91a0b005defec7436f9fd9b44c39
4
- data.tar.gz: be583138932ff4fe697f296bb57f691898f299fe6a1af7f2ed7be85f5d7cfffc
3
+ metadata.gz: bfad6f29472a1ffc004d85c66182f379905f04d968324acba49ded7c3bf277c9
4
+ data.tar.gz: b351216f297f3c692f42fd44e0ba917a3405e677c567fa9206d6d8f4cba936c3
5
5
  SHA512:
6
- metadata.gz: 36437067c9f6a566b9894cb2f377a337a2d8aec466b46f856804027debc2ab583974cf325b45b71ef9e33f88235a9f2b3a2042dd2546119414f4931a3a0b0525
7
- data.tar.gz: 00cf30e71c1fe476701dd362e97383ed9d89b7f5eeb24c83459d45dcae5ad3d2ba9a49737bc1263463191bd63640ff3f3b273b91384bcd2826228cecbb4c96db
6
+ metadata.gz: d3aa9e8b0438d4f4f0dd59f286685f4d2e0954ca6b50f6aed8a02c6e819e9be2abc7886468931e2e9cf2ea0bf70525decd5364468b99b30944dc74e2e40e08b2
7
+ data.tar.gz: 053b1593c4b4ed4ab05cbc1cc481361a65d9dac6b05ae1ec5f03ec06c931f712ababb7665e7cfefe6d129e2d4a9a3d7717772ca9b7502a28bed1db5e044f3946
@@ -1,111 +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 = (double) 10.0;
22
- for (i = 1; i < precision; i++) { rounding_factor *= (double) 10.0; }
23
-
24
- double multiplied_num = num * (double) 10.0;
25
- for (i = 1; i < precision; i++) { multiplied_num *= (double) 10.0; }
26
-
27
- rounded_num = round(multiplied_num) / rounding_factor;
28
- } else {
29
- rounded_num = round(num);
11
+ // Copy all the precision digits
12
+ for (unsigned int i = 0; i < precision; i++) {
13
+ *p-- = '0' + (v % 10);
14
+ v /= 10;
30
15
  }
31
16
 
32
- if (precision > 0) { precision++; } // adjust for .
33
-
34
- numlen = snprintf(numstr, 100, formatstr, rounded_num);
35
- digits_to_comma = numlen - precision;
36
- if (numstr[0] == '-') { digits_to_comma--; }
37
-
38
- strl = numlen + (digits_to_comma / 3);
39
- if ((digits_to_comma % 3) == 0) {
40
- strl--;
41
- }
42
-
43
- if (strncmp(numstr, "-0.00000", strl) == 0) {
44
- return rb_str_new("0.00000", 1 + precision);
45
- }
46
-
47
- stri = strl;
48
- str[stri] = 0;
49
- for (i = 1; i <= (precision + 1); i++) {
50
- str[stri - i] = numstr[numlen - i];
17
+ // Add the dot
18
+ if (precision > 0) {
19
+ *p-- = '.';
51
20
  }
52
21
 
53
- stri -= (precision + 2);
54
- numlen -= (precision + 1);
55
- 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;
56
27
 
57
- while ((numlen - numi) >= 1) {
58
- if ((numi % 3) == 0) {
59
- str[stri] = ',';
60
- stri--;
28
+ if ((++digits % 3) == 0 && v) {
29
+ *p-- = ',';
61
30
  }
62
- str[stri] = numstr[numlen - numi];
63
- stri--;
64
- numi++;
65
- }
66
-
67
- if (numstr[0] == '-') {
68
- str[0] = '-';
69
- } else {
70
- if ((numi % 3) == 0) {
71
- str[1] = ',';
72
- }
73
- str[0] = numstr[0];
74
- }
75
- return rb_str_new2(str);
76
- }
31
+ } while(v);
77
32
 
78
- struct ae_fast_decimal_formatter {
79
- double num;
80
- int precision;
81
- };
33
+ // Finally, add the - if we started with a negative number
34
+ if (value < 0) *p-- = '-';
82
35
 
83
- static void ae_fast_decimal_formatter_free(void *p) {}
84
-
85
- static VALUE ae_fast_decimal_formatter_alloc(VALUE klass) {
86
- VALUE obj;
87
- struct ae_fast_decimal_formatter *ptr;
88
-
89
- obj = Data_Make_Struct(klass, struct ae_fast_decimal_formatter, NULL, ae_fast_decimal_formatter_free, ptr);
90
-
91
- ptr->num = 0.0;
92
- ptr->precision = 0;
93
-
94
- return obj;
95
- }
36
+ // And adjust the p to undo the last p--
37
+ p++;
96
38
 
97
- static VALUE ae_fast_decimal_formatter_init(VALUE self, VALUE number, VALUE precision) {
98
- struct ae_fast_decimal_formatter *ptr;
99
- Data_Get_Struct(self, struct ae_fast_decimal_formatter, ptr);
100
- ptr->num = NUM2DBL(number);
101
- ptr->precision = NUM2UINT(precision);
102
- return self;
39
+ return p;
103
40
  }
104
41
 
105
- static VALUE ae_fast_decimal_formatter_format(VALUE self) {
106
- struct ae_fast_decimal_formatter *ptr;
107
- Data_Get_Struct(self, struct ae_fast_decimal_formatter, ptr);
108
- 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));
109
57
  }
110
58
 
111
59
  void Init_ae_fast_decimal_formatter(void) {
@@ -113,7 +61,5 @@ void Init_ae_fast_decimal_formatter(void) {
113
61
 
114
62
  cFastDecimalFormatter = rb_const_get(rb_cObject, rb_intern("AeFastDecimalFormatter"));
115
63
 
116
- rb_define_alloc_func(cFastDecimalFormatter, ae_fast_decimal_formatter_alloc);
117
- rb_define_method(cFastDecimalFormatter, "initialize", ae_fast_decimal_formatter_init, 2);
118
- 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);
119
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"
@@ -1,3 +1,3 @@
1
1
  class AeFastDecimalFormatter
2
- VERSION = '1.0.0'
2
+ VERSION = '2.0.0'
3
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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ae_fast_decimal_formatter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - AppFolio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-23 00:00:00.000000000 Z
11
+ date: 2022-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
86
  requirements: []
87
- rubygems_version: 3.3.3
87
+ rubygems_version: 3.1.6
88
88
  signing_key:
89
89
  specification_version: 4
90
90
  summary: Efficiently format decimal number.