line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################################### |
2
|
|
|
|
|
|
|
# Numbers to Words Module for Perl. |
3
|
|
|
|
|
|
|
# Copyright (C) 1996-2016, Lester Hightower |
4
|
|
|
|
|
|
|
############################################################################### |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Lingua::EN::Nums2Words; |
7
|
|
|
|
|
|
|
require 5.000; |
8
|
|
|
|
|
|
|
require Exporter; |
9
|
1
|
|
|
1
|
|
326
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
27
|
|
10
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1867
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our @ISA=qw(Exporter); |
13
|
|
|
|
|
|
|
our @EXPORT=qw(num2word num2usdollars num2word_ordinal num2word_short_ordinal); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = "1.16"; |
16
|
0
|
|
|
0
|
0
|
0
|
sub Version { $VERSION; } |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
############################################################################### |
19
|
|
|
|
|
|
|
# Private File-Global Variables ############################################### |
20
|
|
|
|
|
|
|
############################################################################### |
21
|
|
|
|
|
|
|
# Initialization Function init_mod_vars() sets up these variables |
22
|
|
|
|
|
|
|
my @Classifications; |
23
|
|
|
|
|
|
|
my @MD; |
24
|
|
|
|
|
|
|
my @Categories; |
25
|
|
|
|
|
|
|
my %CardinalToOrdinalMapping; |
26
|
|
|
|
|
|
|
my @CardinalToShortOrdinalMapping; |
27
|
|
|
|
|
|
|
my $word_case = 'upper'; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# At module load time, initialize our static, file-global variables. |
30
|
|
|
|
|
|
|
# We use these file-global variables to increase performance when one |
31
|
|
|
|
|
|
|
# needs to compute many iterations for numbers to words. The alternative |
32
|
|
|
|
|
|
|
# would be to re-instantiate the never-changing variables over and over. |
33
|
|
|
|
|
|
|
&init_mod_vars; |
34
|
|
|
|
|
|
|
############################################################################### |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
############################################################################### |
37
|
|
|
|
|
|
|
# Public Functions ############################################################ |
38
|
|
|
|
|
|
|
############################################################################### |
39
|
|
|
|
|
|
|
sub set_case($) { |
40
|
2
|
|
|
2
|
0
|
45
|
my $case=lc(shift @_); |
41
|
2
|
|
|
|
|
7
|
my @cases=qw(upper lower); |
42
|
2
|
50
|
|
|
|
41
|
if (scalar(grep(/^$case$/, @cases)) != 1) { |
43
|
0
|
|
|
|
|
0
|
die __PACKAGE__.":set_case() only accepts these optons: " . |
44
|
|
|
|
|
|
|
join(", ", @cases) . "\n"; |
45
|
|
|
|
|
|
|
} |
46
|
2
|
|
|
|
|
9
|
$word_case=$case; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub case($) { |
50
|
148
|
|
|
148
|
0
|
220
|
my $str=shift @_; |
51
|
148
|
100
|
|
|
|
262
|
if ($word_case eq 'upper') { |
|
|
50
|
|
|
|
|
|
52
|
74
|
|
|
|
|
225
|
return uc($str); |
53
|
|
|
|
|
|
|
} elsif ($word_case eq 'lower') { |
54
|
74
|
|
|
|
|
203
|
return lc($str); |
55
|
|
|
|
|
|
|
} else { |
56
|
0
|
|
|
|
|
0
|
retrn $str; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub num2word { |
61
|
88
|
|
|
88
|
0
|
648
|
my $Number = shift(@_); |
62
|
88
|
|
|
|
|
137
|
return(case(&num2word_internal($Number, 0))); |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub num2usdollars { |
66
|
26
|
|
|
26
|
0
|
312
|
my $Number = shift(@_); |
67
|
|
|
|
|
|
|
# OK, lets do some parsing of what we were handed to be nice and |
68
|
|
|
|
|
|
|
# flexible to the users of this API |
69
|
26
|
|
|
|
|
44
|
$Number=~s/^\$//; # Kill leading dollar sign |
70
|
|
|
|
|
|
|
# Get the decimal into hundreths |
71
|
|
|
|
|
|
|
# NOTE: sprintf(%f) fails on very large decimal numbers, so we use |
72
|
|
|
|
|
|
|
# our RoundToTwoDecimalPlaces() instead of a sprintf() call. |
73
|
|
|
|
|
|
|
#$Number=sprintf("%0.02f", $Number); |
74
|
26
|
|
|
|
|
75
|
$Number = RoundToTwoDecimalPlaces($Number); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# Get the num2word version |
77
|
26
|
|
|
|
|
50
|
my $Final=num2word_internal($Number, 1); |
78
|
|
|
|
|
|
|
# Now whack the num2word version into a US dollar version |
79
|
26
|
|
|
|
|
38
|
my $dollar_verb='DOLLAR'; |
80
|
26
|
50
|
|
|
|
113
|
if (abs(int($Number)) != 1) { $dollar_verb .= 'S'; } |
|
26
|
|
|
|
|
39
|
|
81
|
26
|
50
|
|
|
|
132
|
if (! ($Final=~s/ AND / $dollar_verb AND /)) { |
82
|
0
|
|
|
|
|
0
|
$Final .= ' DOLLAR'; |
83
|
0
|
0
|
|
|
|
0
|
if (abs($Number) != 1) { $Final .='S'; } |
|
0
|
|
|
|
|
0
|
|
84
|
|
|
|
|
|
|
} else { |
85
|
26
|
|
|
|
|
179
|
$Final=~s/(HUNDREDTH|TENTH)([S]?)/CENT$2/; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Return the verbiage to the calling program |
89
|
26
|
|
|
|
|
54
|
return(case($Final)); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub num2word_ordinal { |
93
|
26
|
|
|
26
|
0
|
281
|
my $Number = shift(@_); |
94
|
26
|
|
|
|
|
35
|
my($CardPartToConvToOrd, $ConvTo); |
95
|
|
|
|
|
|
|
# Get the num2word version |
96
|
26
|
|
|
|
|
37
|
my $Final=num2word_internal($Number, 0); |
97
|
|
|
|
|
|
|
# Now whack the num2word version into a US dollar version |
98
|
|
|
|
|
|
|
|
99
|
26
|
100
|
|
|
|
99
|
if ($Final=~/ AND /) { |
100
|
18
|
100
|
|
|
|
110
|
if ($Final =~ m/[- ]([A-Z]+) AND/) { |
101
|
16
|
|
|
|
|
34
|
$CardPartToConvToOrd=$1; |
102
|
16
|
50
|
|
|
|
34
|
if (defined($CardinalToOrdinalMapping{$CardPartToConvToOrd})) { |
103
|
16
|
|
|
|
|
21
|
$ConvTo=$CardinalToOrdinalMapping{$CardPartToConvToOrd}; |
104
|
|
|
|
|
|
|
} else { |
105
|
0
|
|
|
|
|
0
|
$ConvTo=''; |
106
|
0
|
|
|
|
|
0
|
warn "NumToWords.pm Missing CardinalToOrdinalMapping -> $CardPartToConvToOrd"; |
107
|
|
|
|
|
|
|
} |
108
|
16
|
|
|
|
|
272
|
$Final =~ s/([- ])$CardPartToConvToOrd AND/$1$ConvTo AND/; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} else { |
111
|
8
|
50
|
|
|
|
45
|
if ($Final =~ m/([A-Z]+)$/) { |
112
|
8
|
|
|
|
|
16
|
$CardPartToConvToOrd=$1; |
113
|
8
|
50
|
|
|
|
15
|
if (defined($CardinalToOrdinalMapping{$CardPartToConvToOrd})) { |
114
|
8
|
|
|
|
|
13
|
$ConvTo=$CardinalToOrdinalMapping{$CardPartToConvToOrd}; |
115
|
|
|
|
|
|
|
} else { |
116
|
0
|
|
|
|
|
0
|
$ConvTo=''; |
117
|
0
|
|
|
|
|
0
|
warn "NumToWords.pm Missing CardinalToOrdinalMapping -> $CardPartToConvToOrd"; |
118
|
|
|
|
|
|
|
} |
119
|
8
|
|
|
|
|
97
|
$Final =~ s/$CardPartToConvToOrd$/$ConvTo/; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Return the verbiage to the calling program |
124
|
26
|
|
|
|
|
102
|
return(case($Final)); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub num2word_short_ordinal { |
128
|
8
|
|
|
8
|
0
|
82
|
my $Number = shift(@_); |
129
|
8
|
50
|
|
|
|
20
|
if ($Number != int($Number)) { |
130
|
0
|
|
|
|
|
0
|
warn "num2word_short_ordinal can only handle integers!\n"; |
131
|
0
|
|
|
|
|
0
|
return($Number); |
132
|
|
|
|
|
|
|
} |
133
|
8
|
|
|
|
|
10
|
$Number=int($Number); |
134
|
8
|
|
|
|
|
12
|
my $least_sig_dig = undef; |
135
|
8
|
|
|
|
|
9
|
my $least_2_sig_dig = undef; |
136
|
8
|
50
|
|
|
|
27
|
if ($Number=~m/([0-9])?([0-9])$/) { |
137
|
8
|
|
|
|
|
15
|
$least_sig_dig=$2; |
138
|
8
|
50
|
|
|
|
12
|
if (defined($1)) { |
139
|
8
|
|
|
|
|
16
|
$least_2_sig_dig=$1.$2; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} else { |
142
|
0
|
|
|
|
|
0
|
warn "num2word_short_ordinal couldn't find least significant int!\n"; |
143
|
0
|
|
|
|
|
0
|
return($Number); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
8
|
100
|
66
|
|
|
28
|
if (defined($least_2_sig_dig) && |
147
|
|
|
|
|
|
|
defined($CardinalToShortOrdinalMapping[$least_2_sig_dig])) { |
148
|
4
|
|
|
|
|
5
|
$Number.=$CardinalToShortOrdinalMapping[$least_2_sig_dig]; |
149
|
|
|
|
|
|
|
} else { |
150
|
4
|
|
|
|
|
7
|
$Number.=$CardinalToShortOrdinalMapping[$least_sig_dig]; |
151
|
|
|
|
|
|
|
} |
152
|
8
|
|
|
|
|
15
|
return(case($Number)); |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
############################################################################### |
156
|
|
|
|
|
|
|
# Private Functions ########################################################### |
157
|
|
|
|
|
|
|
############################################################################### |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub num2word_internal { |
160
|
140
|
|
|
140
|
0
|
185
|
my $Number = shift(@_); |
161
|
140
|
|
|
|
|
167
|
my $KeepTrailingZeros = shift(@_); |
162
|
140
|
|
|
|
|
260
|
my($ClassificationIndex, %Breakdown, $Index); |
163
|
140
|
|
|
|
|
0
|
my($NegativeFlag, $Classification); |
164
|
140
|
|
|
|
|
215
|
my($Word, $Final, $DecimalVerbiage) = ("", "", ""); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Hand the number off to a function to get the verbiage |
167
|
|
|
|
|
|
|
# for what appears after the decimal |
168
|
140
|
|
|
|
|
208
|
$DecimalVerbiage = &HandleDecimal($Number, $KeepTrailingZeros); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# Determine if the number is negative and if so, |
171
|
|
|
|
|
|
|
# remember that fact and then make it positive |
172
|
140
|
100
|
66
|
|
|
472
|
if (length($Number) && ($Number < 0)) { |
173
|
18
|
|
|
|
|
29
|
$NegativeFlag=1; $Number = $Number * -1; |
|
18
|
|
|
|
|
25
|
|
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# Take only the integer part of the number for the |
177
|
|
|
|
|
|
|
# calculation of the integer part verbiage |
178
|
|
|
|
|
|
|
# NOTE: Changed to regex 06/08/1998 by LHH because the int() |
179
|
|
|
|
|
|
|
# was preventing the code from doing very large numbers |
180
|
|
|
|
|
|
|
# by restricting the precision of $Number. |
181
|
|
|
|
|
|
|
# $Number = int($Number); |
182
|
140
|
100
|
|
|
|
453
|
if ($Number =~ /^([0-9]*)\./) { |
183
|
62
|
|
|
|
|
162
|
$Number = $1; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Go through each of the @Classifications breaking off each |
187
|
|
|
|
|
|
|
# three number pair from right to left corresponding to |
188
|
|
|
|
|
|
|
# each of the @Classifications |
189
|
140
|
|
|
|
|
160
|
$ClassificationIndex = 0; |
190
|
140
|
|
|
|
|
215
|
while (length($Number) > 0) { |
191
|
314
|
100
|
|
|
|
477
|
if (length($Number) > 2) { |
192
|
220
|
|
|
|
|
401
|
$Breakdown{$Classifications[$ClassificationIndex]} = |
193
|
|
|
|
|
|
|
substr($Number, length($Number) - 3); |
194
|
220
|
|
|
|
|
304
|
$Number = substr($Number, 0, length($Number) - 3); |
195
|
|
|
|
|
|
|
} else { |
196
|
94
|
|
|
|
|
229
|
$Breakdown{$Classifications[$ClassificationIndex]} = $Number; |
197
|
94
|
|
|
|
|
117
|
$Number = ""; |
198
|
|
|
|
|
|
|
} |
199
|
314
|
|
|
|
|
584
|
$ClassificationIndex++; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Go over each of the @Classifications producing the verbiage |
203
|
|
|
|
|
|
|
# for each and adding each to the verbiage stack ($Final) |
204
|
140
|
|
|
|
|
176
|
$Index=0; |
205
|
140
|
|
|
|
|
197
|
foreach $Classification (@Classifications) { |
206
|
|
|
|
|
|
|
# If the value of these three digits == 0 then they can be ignored |
207
|
3080
|
100
|
100
|
|
|
5519
|
if ( (! defined($Breakdown{$Classification})) || |
208
|
2776
|
|
|
|
|
2923
|
($Breakdown{$Classification} < 1) ) { $Index++; next;} |
|
2776
|
|
|
|
|
3482
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Retrieves the $Word for these three digits |
211
|
304
|
|
|
|
|
450
|
$Word = &HandleThreeDigit($Breakdown{$Classification}); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Leaves "$Classifications[0] off of HUNDREDs-TENs-ONEs numbers |
214
|
304
|
100
|
|
|
|
490
|
if ($Index > 0) { |
215
|
178
|
|
|
|
|
243
|
$Word .= " " . $Classification; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Adds this $Word to the $Final and determines if it needs a comma |
219
|
304
|
100
|
|
|
|
468
|
if (length($Final) > 0) { |
220
|
178
|
|
|
|
|
319
|
$Final = $Word . ", " . $Final; |
221
|
|
|
|
|
|
|
} else { |
222
|
126
|
|
|
|
|
201
|
$Final = $Word; |
223
|
|
|
|
|
|
|
} |
224
|
304
|
|
|
|
|
389
|
$Index++; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# If our $Final verbiage is an empty string then our original number |
228
|
|
|
|
|
|
|
# was zero, so make the verbiage reflect that. |
229
|
140
|
100
|
|
|
|
255
|
if (length($Final) == 0) { |
230
|
14
|
|
|
|
|
17
|
$Final = "ZERO"; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# If we marked the number as negative in the beginning, make the |
234
|
|
|
|
|
|
|
# verbiage reflect that by prepending NEGATIVE |
235
|
140
|
100
|
|
|
|
187
|
if ($NegativeFlag) { |
236
|
18
|
|
|
|
|
40
|
$Final = "NEGATIVE " . $Final; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Now append the decimal portion of the verbiage calculated at the |
240
|
|
|
|
|
|
|
# beginning if there is any |
241
|
140
|
100
|
|
|
|
259
|
if (length($DecimalVerbiage) > 0) { |
242
|
62
|
|
|
|
|
106
|
$Final .= " AND " . $DecimalVerbiage; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Return the verbiage to the calling program |
246
|
140
|
|
|
|
|
355
|
return($Final); |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Helper function which handles three digits from the @Classifications |
250
|
|
|
|
|
|
|
# level (THOUSANDS, MILLIONS, etc) - Deals with the HUNDREDs |
251
|
|
|
|
|
|
|
sub HandleThreeDigit { |
252
|
304
|
|
|
304
|
0
|
438
|
my $Number = shift(@_); |
253
|
304
|
|
|
|
|
398
|
my($Hundreds, $HundredVerbiage, $TenVerbiage, $Verbiage); |
254
|
|
|
|
|
|
|
|
255
|
304
|
100
|
|
|
|
461
|
if (length($Number) > 2) { |
256
|
220
|
|
|
|
|
274
|
$Hundreds = substr($Number, 0, 1); |
257
|
220
|
|
|
|
|
280
|
$HundredVerbiage = &HandleTwoDigit($Hundreds); |
258
|
220
|
100
|
|
|
|
408
|
if (length($HundredVerbiage) > 0) { |
259
|
200
|
|
|
|
|
248
|
$HundredVerbiage .= " HUNDRED"; |
260
|
|
|
|
|
|
|
} |
261
|
220
|
|
|
|
|
280
|
$Number = substr($Number, 1); |
262
|
|
|
|
|
|
|
} |
263
|
304
|
|
|
|
|
400
|
$TenVerbiage = &HandleTwoDigit($Number); |
264
|
304
|
100
|
100
|
|
|
727
|
if ( (defined($HundredVerbiage)) && (length($HundredVerbiage) > 0) ) { |
265
|
200
|
|
|
|
|
263
|
$Verbiage = $HundredVerbiage; |
266
|
200
|
50
|
|
|
|
310
|
if (length($TenVerbiage)) { $Verbiage .= " " . $TenVerbiage; } |
|
200
|
|
|
|
|
330
|
|
267
|
|
|
|
|
|
|
} else { |
268
|
104
|
|
|
|
|
141
|
$Verbiage=$TenVerbiage; |
269
|
|
|
|
|
|
|
} |
270
|
304
|
|
|
|
|
491
|
return($Verbiage); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# Helper function which handles two digits (from 99 to 0) |
274
|
|
|
|
|
|
|
sub HandleTwoDigit { |
275
|
524
|
|
|
524
|
0
|
683
|
my $Number = shift(@_); |
276
|
524
|
|
|
|
|
591
|
my($Verbiage, $Tens, $Ones); |
277
|
|
|
|
|
|
|
|
278
|
524
|
100
|
|
|
|
707
|
if (length($Number) < 2) { |
279
|
284
|
|
|
|
|
563
|
return($MD[$Number]); |
280
|
|
|
|
|
|
|
} else { |
281
|
240
|
100
|
|
|
|
373
|
if ($Number < 20) { |
282
|
62
|
|
|
|
|
103
|
return($MD[$Number]); |
283
|
|
|
|
|
|
|
} else { |
284
|
178
|
|
|
|
|
253
|
$Tens = substr($Number, 0, 1); |
285
|
178
|
|
|
|
|
226
|
$Tens = $Tens * 10; |
286
|
178
|
|
|
|
|
258
|
$Ones = substr($Number, 1, 1); |
287
|
178
|
100
|
|
|
|
270
|
if (length($MD[$Ones]) > 0) { |
288
|
168
|
|
|
|
|
281
|
$Verbiage = $MD[$Tens] . "-" . $MD[$Ones]; |
289
|
|
|
|
|
|
|
} else { |
290
|
10
|
|
|
|
|
14
|
$Verbiage = $MD[$Tens]; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
} |
294
|
178
|
|
|
|
|
294
|
return($Verbiage); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
sub HandleDecimal { |
298
|
140
|
|
|
140
|
0
|
191
|
my $DecNumber = shift(@_); |
299
|
140
|
|
|
|
|
162
|
my $KeepTrailingZeros = shift(@_); |
300
|
140
|
|
|
|
|
158
|
my $Verbiage = ""; |
301
|
140
|
|
|
|
|
176
|
my $CategoriesIndex = 0; |
302
|
140
|
|
|
|
|
155
|
my $CategoryVerbiage = ''; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# I'm choosing to do this string-wise rather than mathematically |
305
|
|
|
|
|
|
|
# because the error in the mathematics can alter the number from |
306
|
|
|
|
|
|
|
# exactly what was sent in for high significance numbers |
307
|
|
|
|
|
|
|
# NOTE: Changed "if" to regex 06/08/1998 by LHH because the int() |
308
|
|
|
|
|
|
|
# was preventing the code from doing very large numbers |
309
|
|
|
|
|
|
|
# by restricting the precision of $Number. |
310
|
140
|
100
|
|
|
|
376
|
if ( ! ($DecNumber =~ /\./) ) { |
311
|
78
|
|
|
|
|
144
|
return(''); |
312
|
|
|
|
|
|
|
} else { |
313
|
62
|
|
|
|
|
150
|
$DecNumber = substr($DecNumber, rindex($DecNumber, '.') + 1); |
314
|
|
|
|
|
|
|
# Trim off any trailing zeros... |
315
|
62
|
100
|
|
|
|
111
|
if (! $KeepTrailingZeros) { $DecNumber =~ s/0+$//; } |
|
36
|
|
|
|
|
68
|
|
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
62
|
|
|
|
|
78
|
$CategoriesIndex = length($DecNumber); |
319
|
62
|
|
|
|
|
93
|
$CategoryVerbiage = $Categories[$CategoriesIndex - 1]; |
320
|
62
|
50
|
33
|
|
|
205
|
if (length($DecNumber) && $DecNumber == 1) { |
321
|
|
|
|
|
|
|
# if the value of what is after the decimal place is one, then |
322
|
|
|
|
|
|
|
# we need to chop the "s" off the end of the $CategoryVerbiage |
323
|
|
|
|
|
|
|
# to make is singular |
324
|
0
|
|
|
|
|
0
|
chop($CategoryVerbiage); |
325
|
|
|
|
|
|
|
} |
326
|
62
|
|
|
|
|
96
|
$Verbiage = &num2word($DecNumber) . " " . $CategoryVerbiage; |
327
|
62
|
|
|
|
|
137
|
return($Verbiage); |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# NOTE: sprintf(%f) fails on very large decimal numbers, thus the |
331
|
|
|
|
|
|
|
# need for RoundToTwoDecimalPlaces(). |
332
|
|
|
|
|
|
|
sub RoundToTwoDecimalPlaces($) { |
333
|
26
|
|
|
26
|
0
|
36
|
my $Number=shift @_; |
334
|
|
|
|
|
|
|
|
335
|
26
|
|
|
|
|
72
|
my($Int,$Dec,$UserScrewUp) = split(/\./, $Number, 3); |
336
|
26
|
50
|
33
|
|
|
58
|
if (defined($UserScrewUp) && length($UserScrewUp)) { |
337
|
0
|
|
|
|
|
0
|
warn "num2usdollars() given invalid value."; } |
338
|
26
|
100
|
|
|
|
50
|
if (! length($Int)) { $Int=0; } |
|
2
|
|
|
|
|
3
|
|
339
|
26
|
100
|
|
|
|
45
|
$Dec = 0 if not defined($Dec); |
340
|
26
|
|
|
|
|
169
|
my $DecPart=int(sprintf("%0.3f", "." . $Dec) * 100 + 0.5); |
341
|
|
|
|
|
|
|
|
342
|
26
|
|
|
|
|
56
|
$Number=$Int . '.' . $DecPart; |
343
|
26
|
|
|
|
|
89
|
return $Number; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# This function initializes our static, file-global variables. |
347
|
|
|
|
|
|
|
sub init_mod_vars { |
348
|
1
|
|
|
1
|
0
|
3
|
@Categories = ( |
349
|
|
|
|
|
|
|
"TENTHS", |
350
|
|
|
|
|
|
|
"HUNDREDTHS", |
351
|
|
|
|
|
|
|
"THOUSANDTHS", |
352
|
|
|
|
|
|
|
"TEN-THOUSANDTHS", |
353
|
|
|
|
|
|
|
"HUNDRED-THOUSANDTHS", |
354
|
|
|
|
|
|
|
"MILLIONTHS", |
355
|
|
|
|
|
|
|
"TEN-MILLIONTHS", |
356
|
|
|
|
|
|
|
"HUNDRED-MILLIONTHS", |
357
|
|
|
|
|
|
|
"BILLIONTHS", |
358
|
|
|
|
|
|
|
"TEN-BILLIONTHS", |
359
|
|
|
|
|
|
|
"HUNDRED-BILLIONTHS", |
360
|
|
|
|
|
|
|
"TRILLIONTHS", |
361
|
|
|
|
|
|
|
"QUADRILLIONTHS", |
362
|
|
|
|
|
|
|
"QUINTILLIONTHS", |
363
|
|
|
|
|
|
|
"SEXTILLIONTHS", |
364
|
|
|
|
|
|
|
"SEPTILLIONTHS", |
365
|
|
|
|
|
|
|
"OCTILLIONTHS", |
366
|
|
|
|
|
|
|
"NONILLIONTHS", |
367
|
|
|
|
|
|
|
"DECILLIONTHS", |
368
|
|
|
|
|
|
|
"UNDECILLIONTHS", |
369
|
|
|
|
|
|
|
"DUODECILLIONTHS", |
370
|
|
|
|
|
|
|
"TREDECILLIONTHS", |
371
|
|
|
|
|
|
|
"QUATTUORDECILLIONTHS", |
372
|
|
|
|
|
|
|
"QUINDECILLIONTHS", |
373
|
|
|
|
|
|
|
"SEXDECILLIONTHS", |
374
|
|
|
|
|
|
|
"SEPTEMDECILLIONTHS", |
375
|
|
|
|
|
|
|
"OCTODECILLIONTHS", |
376
|
|
|
|
|
|
|
"NOVEMDECILLIONTHS", |
377
|
|
|
|
|
|
|
"VIGINTILLIONTHS" |
378
|
|
|
|
|
|
|
); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
################################################### |
381
|
|
|
|
|
|
|
|
382
|
1
|
|
|
|
|
2
|
$MD[0] = ""; |
383
|
1
|
|
|
|
|
4
|
$MD[1] = "ONE"; |
384
|
1
|
|
|
|
|
2
|
$MD[2] = "TWO"; |
385
|
1
|
|
|
|
|
1
|
$MD[3] = "THREE"; |
386
|
1
|
|
|
|
|
2
|
$MD[4] = "FOUR"; |
387
|
1
|
|
|
|
|
2
|
$MD[5] = "FIVE"; |
388
|
1
|
|
|
|
|
1
|
$MD[6] = "SIX"; |
389
|
1
|
|
|
|
|
1
|
$MD[7] = "SEVEN"; |
390
|
1
|
|
|
|
|
2
|
$MD[8] = "EIGHT"; |
391
|
1
|
|
|
|
|
4
|
$MD[9] = "NINE"; |
392
|
1
|
|
|
|
|
2
|
$MD[10] = "TEN"; |
393
|
1
|
|
|
|
|
1
|
$MD[11] = "ELEVEN"; |
394
|
1
|
|
|
|
|
3
|
$MD[12] = "TWELVE"; |
395
|
1
|
|
|
|
|
1
|
$MD[13] = "THIRTEEN"; |
396
|
1
|
|
|
|
|
2
|
$MD[14] = "FOURTEEN"; |
397
|
1
|
|
|
|
|
1
|
$MD[15] = "FIFTEEN"; |
398
|
1
|
|
|
|
|
2
|
$MD[16] = "SIXTEEN"; |
399
|
1
|
|
|
|
|
1
|
$MD[17] = "SEVENTEEN"; |
400
|
1
|
|
|
|
|
2
|
$MD[18] = "EIGHTEEN"; |
401
|
1
|
|
|
|
|
1
|
$MD[19] = "NINETEEN"; |
402
|
1
|
|
|
|
|
2
|
$MD[20] = "TWENTY"; |
403
|
1
|
|
|
|
|
1
|
$MD[30] = "THIRTY"; |
404
|
1
|
|
|
|
|
2
|
$MD[40] = "FORTY"; |
405
|
1
|
|
|
|
|
1
|
$MD[50] = "FIFTY"; |
406
|
1
|
|
|
|
|
2
|
$MD[60] = "SIXTY"; |
407
|
1
|
|
|
|
|
1
|
$MD[70] = "SEVENTY"; |
408
|
1
|
|
|
|
|
2
|
$MD[80] = "EIGHTY"; |
409
|
1
|
|
|
|
|
1
|
$MD[90] = "NINETY"; |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
################################################### |
412
|
|
|
|
|
|
|
|
413
|
1
|
|
|
|
|
3
|
@Classifications = ( |
414
|
|
|
|
|
|
|
"HUNDREDs-TENs-ONEs", |
415
|
|
|
|
|
|
|
"THOUSAND", |
416
|
|
|
|
|
|
|
"MILLION", |
417
|
|
|
|
|
|
|
"BILLION", |
418
|
|
|
|
|
|
|
"TRILLION", |
419
|
|
|
|
|
|
|
"QUADRILLION", |
420
|
|
|
|
|
|
|
"QUINTILLION", |
421
|
|
|
|
|
|
|
"SEXTILLION", |
422
|
|
|
|
|
|
|
"SEPTILLION", |
423
|
|
|
|
|
|
|
"OCTILLION", |
424
|
|
|
|
|
|
|
"NONILLION", |
425
|
|
|
|
|
|
|
"DECILLION", |
426
|
|
|
|
|
|
|
"UNDECILLION", |
427
|
|
|
|
|
|
|
"DUODECILLION", |
428
|
|
|
|
|
|
|
"TREDECILLION", |
429
|
|
|
|
|
|
|
"QUATTUORDECILLION", |
430
|
|
|
|
|
|
|
"QUINDECILLION", |
431
|
|
|
|
|
|
|
"SEXDECILLION", |
432
|
|
|
|
|
|
|
"SEPTEMDECILLION", |
433
|
|
|
|
|
|
|
"OCTODECILLION", |
434
|
|
|
|
|
|
|
"NOVEMDECILLION", |
435
|
|
|
|
|
|
|
"VIGINTILLION" |
436
|
|
|
|
|
|
|
); |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
################################################### |
440
|
|
|
|
|
|
|
|
441
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'ZERO'} = "ZEROTH"; |
442
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'ONE'} = "FIRST"; |
443
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'TWO'} = "SECOND"; |
444
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'THREE'} = "THIRD"; |
445
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'FOUR'} = "FOURTH"; |
446
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'FIVE'} = "FIFTH"; |
447
|
1
|
|
|
|
|
11
|
$CardinalToOrdinalMapping{'SIX'} = "SIXTH"; |
448
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'SEVEN'} = "SEVENTH"; |
449
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'EIGHT'} = "EIGHTH"; |
450
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'NINE'} = "NINTH"; |
451
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'TEN'} = "TENTH"; |
452
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'ELEVEN'} = "ELEVENTH"; |
453
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'TWELVE'} = "TWELFTH"; |
454
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'THIRTEEN'} = "THIRTEENTH"; |
455
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'FOURTEEN'} = "FOURTEENTH"; |
456
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'FIFTEEN'} = "FIFTEENTH"; |
457
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'SIXTEEN'} = "SIXTEENTH"; |
458
|
1
|
|
|
|
|
5
|
$CardinalToOrdinalMapping{'SEVENTEEN'} = "SEVENTEENTH"; |
459
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'EIGHTEEN'} = "EIGHTEENTH"; |
460
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'NINETEEN'} = "NINETEENTH"; |
461
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'TWENTY'} = "TWENTIETH"; |
462
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'THIRTY'} = "THIRTIETH"; |
463
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'FORTY'} = "FORTIETH"; |
464
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'FIFTY'} = "FIFTIETH"; |
465
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'SIXTY'} = "SIXTIETH"; |
466
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'SEVENTY'} = "SEVENTIETH"; |
467
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'EIGHTY'} = "EIGHTIETH"; |
468
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'NINETY'} = "NINETIETH"; |
469
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'HUNDRED'} = "HUNDREDTH"; |
470
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'THOUSAND'} = "THOUSANDTH"; |
471
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'MILLION'} = "MILLIONTH"; |
472
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'BILLION'} = "BILLIONTH"; |
473
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'TRILLION'} = "TRILLIONTH"; |
474
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'QUADRILLION'} = "QUADRILLIONTH"; |
475
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'QUINTILLION'} = "QUINTILLIONTH"; |
476
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'SEXTILLION'} = "SEXTILLIONTH"; |
477
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'SEPTILLION'} = "SEPTILLIONTH"; |
478
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'OCTILLION'} = "OCTILLIONTH"; |
479
|
1
|
|
|
|
|
3
|
$CardinalToOrdinalMapping{'NONILLION'} = "NONILLIONTH"; |
480
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'DECILLION'} = "DECILLIONTH"; |
481
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'TREDECILLION'} = "TREDECILLIONTH"; |
482
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'QUATTUORDECILLION'} = "QUATTUORDECILLIONTH"; |
483
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'QUINDECILLION'} = "QUINDECILLIONTH"; |
484
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'SEXDECILLION'} = "SEXDECILLIONTH"; |
485
|
1
|
|
|
|
|
6
|
$CardinalToOrdinalMapping{'SEPTEMDECILLION'} = "SEPTEMDECILLIONTH"; |
486
|
1
|
|
|
|
|
1
|
$CardinalToOrdinalMapping{'OCTODECILLION'} = "OCTODECILLIONTH"; |
487
|
1
|
|
|
|
|
2
|
$CardinalToOrdinalMapping{'NOVEMDECILLION'} = "NOVEMDECILLIONTH"; |
488
|
1
|
|
|
|
|
6
|
$CardinalToOrdinalMapping{'VIGINTILLION'} = "VIGINTILLIONTH"; |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
################################################### |
491
|
1
|
|
|
|
|
2
|
$CardinalToShortOrdinalMapping[0]='th'; |
492
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[1]='st'; |
493
|
1
|
|
|
|
|
2
|
$CardinalToShortOrdinalMapping[11]='th'; # Special for low teens |
494
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[2]='nd'; |
495
|
1
|
|
|
|
|
2
|
$CardinalToShortOrdinalMapping[12]='th'; # Special for low teens |
496
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[3]='rd'; |
497
|
1
|
|
|
|
|
2
|
$CardinalToShortOrdinalMapping[13]='th'; # Special for low teens |
498
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[4]='th'; |
499
|
1
|
|
|
|
|
2
|
$CardinalToShortOrdinalMapping[5]='th'; |
500
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[6]='th'; |
501
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[7]='th'; |
502
|
1
|
|
|
|
|
1
|
$CardinalToShortOrdinalMapping[8]='th'; |
503
|
1
|
|
|
|
|
2
|
$CardinalToShortOrdinalMapping[9]='th'; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
1; |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# PERL POD #################################################################### |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head1 NAME |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Lingua::EN::Nums2Words - generate English verbiage from numerical values |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head1 SYNOPSIS |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
use Lingua::EN::Nums2Words; |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
$Number = 42; |
521
|
|
|
|
|
|
|
$Verbiage = num2word($Number); |
522
|
|
|
|
|
|
|
$Verbiage = num2word_ordinal($Number); |
523
|
|
|
|
|
|
|
$Verbiage = num2word_short_ordinal($Number); |
524
|
|
|
|
|
|
|
$Verbiage = num2usdollars($Number); |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head1 DESCRIPTION |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
This module provides functions that can be used to generate English |
529
|
|
|
|
|
|
|
verbiage for numbers. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
To the best of my knowledge, this code can handle every real value |
532
|
|
|
|
|
|
|
from negative infinity to positive infinity. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
This module makes verbiage in "short scales" (1,000,000,000 is "one billion" |
535
|
|
|
|
|
|
|
rather than "one thousand million"). For details see this Wikipedia article: |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
L |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head1 SUBROUTINES |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
The following code illustrates use of the four functions in this module: |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
use Lingua::EN::Nums2Words; |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
$age = 45; |
546
|
|
|
|
|
|
|
print "I am ", num2word($age), " years old.\n"; |
547
|
|
|
|
|
|
|
print "I've had my ", num2word_ordinal($age), " birthday.\n"; |
548
|
|
|
|
|
|
|
print "I'm in my ", num2word_short_ordinal($age+1), " year.\n"; |
549
|
|
|
|
|
|
|
print "Pay me ", num2usdollars($age), ".\n"; |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
This prints out: |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
I am FORTY-FIVE years old. |
554
|
|
|
|
|
|
|
I've had my FORTY-FIFTH birthday. |
555
|
|
|
|
|
|
|
I'm in my 46th year. |
556
|
|
|
|
|
|
|
Pay me FORTY-FIVE DOLLARS AND ZERO CENTS. |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
As shown above, the default is to return uppercase words. If you would |
559
|
|
|
|
|
|
|
prefer to have lowercase words returned, make this call once, early in |
560
|
|
|
|
|
|
|
your program: |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
Lingua::EN::Nums2Words::set_case('lower'); # Accepts upper|lower |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 COPYRIGHT |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
Copyright (C) 1996-2016, Lester H. Hightower, Jr. |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head1 LICENSE |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
As of version 1.13, this software is licensed under the OSI certified |
571
|
|
|
|
|
|
|
Artistic License, one of the licenses of Perl itself. |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
L |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=cut |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
############################################################################### |
578
|
|
|
|
|
|
|
|