nio 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/Manifest.txt +1 -2
- data/README.txt +519 -560
- data/Rakefile +1 -0
- data/lib/nio.rb +6 -7
- data/lib/nio/fmt.rb +378 -437
- data/lib/nio/repdec.rb +31 -31
- data/lib/nio/rtnlzr.rb +88 -81
- data/lib/nio/sugar.rb +69 -99
- data/lib/nio/version.rb +1 -1
- data/tasks/nuweb.rake +53 -33
- data/test/{test_helper.rb → helper.rb} +5 -0
- data/test/test_fmt.rb +98 -100
- data/test/test_repdec.rb +3 -5
- data/test/test_rtnlzr.rb +66 -44
- data/test/test_tools.rb +2 -2
- metadata +17 -7
- data/lib/nio/flttol.rb +0 -669
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.2.4 2009-08-05
|
2
|
+
|
3
|
+
* New features
|
4
|
+
- Support for Flt types DecNum and BinNum.
|
5
|
+
* Refactoring
|
6
|
+
- Code has been reorganized to use the Tolerance and Conversion
|
7
|
+
algorithms that have been moved to the Flt library.
|
8
|
+
|
1
9
|
== 0.2.3 2008-12-23
|
2
10
|
|
3
11
|
* Minor enhancements
|
data/Manifest.txt
CHANGED
@@ -7,7 +7,6 @@ Rakefile
|
|
7
7
|
lib/nio.rb
|
8
8
|
lib/nio/version.rb
|
9
9
|
lib/nio/repdec.rb
|
10
|
-
lib/nio/flttol.rb
|
11
10
|
lib/nio/tools.rb
|
12
11
|
lib/nio/rtnlzr.rb
|
13
12
|
lib/nio/fmt.rb
|
@@ -15,7 +14,7 @@ lib/nio/sugar.rb
|
|
15
14
|
setup.rb
|
16
15
|
tasks/nuweb.rake
|
17
16
|
test/data.yaml
|
18
|
-
test/
|
17
|
+
test/helper.rb
|
19
18
|
test/test_tools.rb
|
20
19
|
test/test_repdec.rb
|
21
20
|
test/test_rtnlzr.rb
|
data/README.txt
CHANGED
@@ -1,560 +1,519 @@
|
|
1
|
-
=Description
|
2
|
-
|
3
|
-
Nio (Numeric input/output) is a Ruby package for text-formatted input/output
|
4
|
-
and conversion of scalar numeric types.
|
5
|
-
|
6
|
-
This library formats numbers as text numerals and reads them back into numeric
|
7
|
-
objects. The numeric types Integer Rational, BigDecimal and Float are supported.
|
8
|
-
The numeral format is controlled with Nio::Fmt objects. Conversion between
|
9
|
-
the numerical types is also provided.
|
10
|
-
|
11
|
-
Nio offers low level services; formats supported are only positional notation and
|
12
|
-
types are only scalar.
|
13
|
-
This means that composite types such as Complex are not supported
|
14
|
-
an that fraction notation (X/Y) is not supported for rationals;
|
15
|
-
those are higher level services that could be implemented using Nio
|
16
|
-
but are not the subject of this package.
|
17
|
-
|
18
|
-
The implementation of Nio is pure Ruby, and not specially fast; but it is complete and accurate.
|
19
|
-
|
20
|
-
Nio has some interesting features, though:
|
21
|
-
|
22
|
-
* Correctly rounded conversions of all supported types to and from
|
23
|
-
positional numerals in any base.
|
24
|
-
* Handling of digit repetitions (repeating decimals or, in general, <i>repeating numerals</i>).
|
25
|
-
With this method rational numbers can be represented exactly as numerals.
|
26
|
-
* Discrimitation of significant digits of the representation of Float in any base.
|
27
|
-
(insignificant digits are those that can take any value without altering the Float value they specify.)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
The
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
For
|
59
|
-
|
60
|
-
*
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
x
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
puts x.nio_write
|
87
|
-
puts x.nio_write(Fmt.mode(:
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
* <tt>:
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
puts
|
117
|
-
puts
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
puts 11.2.nio_write(fmt.
|
123
|
-
puts
|
124
|
-
puts
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
puts
|
129
|
-
puts
|
130
|
-
puts
|
131
|
-
puts
|
132
|
-
|
133
|
-
The
|
134
|
-
|
135
|
-
puts
|
136
|
-
puts
|
137
|
-
|
138
|
-
puts
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
puts
|
146
|
-
puts
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
puts
|
154
|
-
puts
|
155
|
-
puts
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
puts
|
161
|
-
|
162
|
-
puts Float
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
puts
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
puts
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
puts Nio.convert(2.0/3, BigDecimal)
|
243
|
-
puts Nio.convert(2
|
244
|
-
puts Nio.convert(
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
fmt =
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
modify it
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
fmt = Fmt.
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
digits (
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
(
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
the
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
Nio.convert(BigDec(355)/226,Float)==(355.0/226) -> true
|
521
|
-
|
522
|
-
Thay may not look very impressive, but is much more accurate than BigDecimal#to_f
|
523
|
-
(at least in Ruby versions up to 1.8.6, mswin32 (specially) and linux) for which:
|
524
|
-
|
525
|
-
BigDecimal('1.234567890123456').to_f == 1.234567890123456 -> false
|
526
|
-
(BigDecimal("355")/226).to_f == (355.0/226.0) -> false
|
527
|
-
|
528
|
-
=License
|
529
|
-
|
530
|
-
This code is free to use under the terms of the GNU GENERAL PUBLIC LICENSE.
|
531
|
-
|
532
|
-
=Contact
|
533
|
-
|
534
|
-
Nio has been developed by Javier Goizueta (mailto:javier@goizueta.info).
|
535
|
-
|
536
|
-
You can contact me through Rubyforge:http://rubyforge.org/sendmessage.php?touser=25432
|
537
|
-
|
538
|
-
|
539
|
-
=More Information
|
540
|
-
|
541
|
-
* <b>What Every Computer Scientist Should Know About Floating-Point Arithmetic</b>
|
542
|
-
David Goldberg
|
543
|
-
- http://docs.sun.com/source/806-3568/ncg_goldberg.html
|
544
|
-
|
545
|
-
* <b>How to Read Floating Point Numbers Accurately</b>
|
546
|
-
William D. Clinger
|
547
|
-
- http://citeseer.ist.psu.edu/224562.html
|
548
|
-
|
549
|
-
* <b>Printing Floating-Point Numbers Quickly and Accurately</b>
|
550
|
-
Robert G. Burger & R. Kent Dybvig
|
551
|
-
- http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
|
552
|
-
|
553
|
-
* <b>Repeating Decimal</b>
|
554
|
-
- http://mathworld.wolfram.com/RepeatingDecimal.html
|
555
|
-
- http://en.wikipedia.org/wiki/Recurring_decimal
|
556
|
-
|
557
|
-
* For <b>floating point rationalization algorithms</b>, see my commented
|
558
|
-
source code for the <tt>rntlzr</tt> module from Nio,
|
559
|
-
which you can download in PDF here:
|
560
|
-
- http://perso.wanadoo.es/jgoizueta/dev/goi/rtnlzr.pdf
|
1
|
+
=Description
|
2
|
+
|
3
|
+
Nio (Numeric input/output) is a Ruby package for text-formatted input/output
|
4
|
+
and conversion of scalar numeric types.
|
5
|
+
|
6
|
+
This library formats numbers as text numerals and reads them back into numeric
|
7
|
+
objects. The numeric types Integer Rational, BigDecimal and Float are supported.
|
8
|
+
The numeral format is controlled with Nio::Fmt objects. Conversion between
|
9
|
+
the numerical types is also provided.
|
10
|
+
|
11
|
+
Nio offers low level services; formats supported are only positional notation and
|
12
|
+
types are only scalar.
|
13
|
+
This means that composite types such as Complex are not supported
|
14
|
+
an that fraction notation (X/Y) is not supported for rationals;
|
15
|
+
those are higher level services that could be implemented using Nio
|
16
|
+
but are not the subject of this package.
|
17
|
+
|
18
|
+
The implementation of Nio is pure Ruby, and not specially fast; but it is complete and accurate.
|
19
|
+
|
20
|
+
Nio has some interesting features, though:
|
21
|
+
|
22
|
+
* Correctly rounded conversions of all supported types to and from
|
23
|
+
positional numerals in any base.
|
24
|
+
* Handling of digit repetitions (repeating decimals or, in general, <i>repeating numerals</i>).
|
25
|
+
With this method rational numbers can be represented exactly as numerals.
|
26
|
+
* Discrimitation of significant digits of the representation of Float in any base.
|
27
|
+
(insignificant digits are those that can take any value without altering the Float value they specify.)
|
28
|
+
|
29
|
+
All definitions are inside the module Nio that acts as a namespace, and methods added
|
30
|
+
to classes outside of Nio have names that begin with the prefix <tt>nio_</tt>.
|
31
|
+
|
32
|
+
Limitations:
|
33
|
+
* The current version does not support UTF-8 or other multi-byte encodings (digits and separators must be one-byte characters).
|
34
|
+
* This code is not very fast, since it is implemented in pure Ruby (no C extensions are used).
|
35
|
+
* Error handling needs to improve also in future versions, specially on input and format parameters checking.
|
36
|
+
|
37
|
+
=Installation
|
38
|
+
|
39
|
+
The easiest way to install Nio is using gems:
|
40
|
+
|
41
|
+
gem install --remote nio
|
42
|
+
|
43
|
+
==Downloads
|
44
|
+
|
45
|
+
The latest version of Nio and its source code can be downloaded form
|
46
|
+
* http://rubyforge.org/project/showfiles.php?group_id=4445
|
47
|
+
|
48
|
+
The source code uses nuweb (a {<i>literate programming system</i>}[http://en.wikipedia.org/wiki/Literate_programming]) to generate
|
49
|
+
the Ruby code for Nio. For more details you can download the nuweb source code package, <tt>nio-source</tt>
|
50
|
+
and the documented source package, <tt>nio-source-pdf</tt>, which contains PDF files.
|
51
|
+
|
52
|
+
|
53
|
+
=Documentation
|
54
|
+
|
55
|
+
For a general introduction and some details, read on below.
|
56
|
+
|
57
|
+
* See the the API to the Nio::Fmt object for all the <b>formatting options</b>.
|
58
|
+
* For <b>type conversions</b> see Fmt.convert().
|
59
|
+
* For some notational <b>shortcuts</b> see nio/sugar.rb[link:files/lib/nio/sugar_rb.html].
|
60
|
+
* To *extend* the formatting to other types see the documentation for the module Nio::Formattable.
|
61
|
+
|
62
|
+
=Basic use
|
63
|
+
|
64
|
+
First we must require the library; we request also the optional nio/sugar.rb for convenient notational shortcuts,
|
65
|
+
and include the module Nio to avoid writing too many Nio::s.
|
66
|
+
|
67
|
+
require 'rubygems'
|
68
|
+
require 'nio'
|
69
|
+
require 'nio/sugar'
|
70
|
+
include Nio
|
71
|
+
|
72
|
+
Let's define a nice number to do some tests:
|
73
|
+
|
74
|
+
x = Math.sqrt(2)+100
|
75
|
+
|
76
|
+
=== Writing
|
77
|
+
|
78
|
+
Now let's try the formatted output:
|
79
|
+
|
80
|
+
puts x.nio_write -> 101.41421356237309
|
81
|
+
puts x.nio_write(Fmt.mode(:fix,4)) -> 101.4142
|
82
|
+
puts x.nio_write(Fmt.mode(:sig,4)) -> 101.4
|
83
|
+
puts x.nio_write(Fmt.mode(:sci,4)) -> 1.014E2
|
84
|
+
puts x.nio_write(Fmt.mode(:gen,4)) -> 101.4
|
85
|
+
puts (1e7*x).nio_write(Fmt.mode(:gen,4)) -> 1.014E9
|
86
|
+
puts (1e7*x).nio_write(Fmt.mode(:gen,4).show_plus) -> +1.014E9
|
87
|
+
puts x.nio_write(Fmt.mode(:gen,:exact)) -> 101.41421356237309
|
88
|
+
|
89
|
+
We've seen some formatting modes:
|
90
|
+
* <tt>:fix</tt> (similar to F in C printf or in Fortran) which shows a number of
|
91
|
+
_fixed_ decimals (4 in the example).
|
92
|
+
* <tt>:sig</tt> is just like :fix, but the number of decimals specified means
|
93
|
+
significant digits.
|
94
|
+
* <tt>:sci</tt> (similar to E in C printf or in Fortran) is for scientific
|
95
|
+
or exponential notation.
|
96
|
+
* <tt>:gen</tt> (similar to G in C printf or in Fortran) is the general notational
|
97
|
+
(which also the default), the number of decimals is for significant digits, and
|
98
|
+
:sig is used if possible; if it would be too long it uses :sci instead.
|
99
|
+
In one example above we used <tt>show_plus</tt> to force the display of the sign.
|
100
|
+
And in the last line we used <tt>:exact</tt> for the number of digits, (which
|
101
|
+
is also the default), and this adjust automatically the number of digits to
|
102
|
+
show the value _exactly_, meaning that if we convert the numeral back to a
|
103
|
+
numeric object of the original type, the same exact value will be produced.
|
104
|
+
|
105
|
+
Now let's see some other formatting aspects. The separators can be defined:
|
106
|
+
|
107
|
+
x *= 1111
|
108
|
+
fmt = Fmt.mode(:fix,4)
|
109
|
+
puts x.nio_write(fmt.sep(',')) -> 112671,1913
|
110
|
+
puts x.nio_write(fmt.sep(',','.',[3])) -> 112.671,1913
|
111
|
+
puts x.nio_write(fmt.sep(',',' ',[2,3])) -> 1 126 71,1913
|
112
|
+
|
113
|
+
The number can be adjusted in a field of specific width:
|
114
|
+
|
115
|
+
fmt = Fmt.mode(:fix,2)
|
116
|
+
puts 11.2.nio_write(fmt.width(8)) -> 11.20
|
117
|
+
puts 11.2.nio_write(fmt.width(8,:right,'*')) -> ***11.20
|
118
|
+
puts 11.2.nio_write(fmt.width(8,:right,'*').show_plus) -> **+11.20
|
119
|
+
puts 11.2.nio_write(fmt.width(8,:internal,'*').show_plus) -> +**11.20
|
120
|
+
puts 11.2.nio_write(fmt.width(8,:left,'*')) -> 11.20***
|
121
|
+
puts 11.2.nio_write(fmt.width(8,:center,'*')) -> *11.20**
|
122
|
+
puts 11.2.nio_write(fmt.pad0s(8)) -> 00011.20
|
123
|
+
puts Rational(112,10).nio_write(fmt.pad0s(8)) -> 00011.20
|
124
|
+
puts 112.nio_write(fmt.pad0s(8)) -> 00112.00
|
125
|
+
|
126
|
+
The numerical base does not need to be 10:
|
127
|
+
|
128
|
+
puts 34222223344.nio_write(fmt.base(16)) -> 7f7cdaff0.00
|
129
|
+
puts x.nio_write(Fmt.base(16)) -> 1b81f.30F6ED22C
|
130
|
+
puts x.nio_write(Fmt.mode(:fix,4).base(2)) -> 11011100000011111.0011
|
131
|
+
puts 1.234333E-23.nio_write(Fmt.base(2).prec(20)) -> 1.1101110110000010011E-77
|
132
|
+
|
133
|
+
The sugar module give us some alternatives for the writing notation:
|
134
|
+
|
135
|
+
puts Fmt << x -> 112671.1912677965
|
136
|
+
puts Fmt.mode(:fix,4) << x -> 112671.1913
|
137
|
+
|
138
|
+
puts Fmt.write(x) -> 112671.1912677965
|
139
|
+
puts Fmt.mode(:fix,4).write(4) -> 4.0000
|
140
|
+
|
141
|
+
===Reading
|
142
|
+
|
143
|
+
To read a numeral we must specify the numeric class we want to convert it to:
|
144
|
+
|
145
|
+
puts Float.nio_read('0.1') -> 0.1
|
146
|
+
puts BigDecimal.nio_read('0.1') -> 0.1E0
|
147
|
+
puts Rational.nio_read('0.1') -> 1/10
|
148
|
+
puts Integer.nio_read('0.1') -> 0
|
149
|
+
|
150
|
+
A format can also be specified, although some aspects (such as the precision)
|
151
|
+
will be ignored.
|
152
|
+
|
153
|
+
puts Float.nio_read('0,1',Fmt.sep(',')) -> 0.1
|
154
|
+
puts Float.nio_read('122.344,1',Fmt.sep(',')) -> 122344.1
|
155
|
+
puts Float.nio_read('122,344.1',Fmt.sep('.')) -> 122344.1
|
156
|
+
|
157
|
+
There are also some sweet alternatives for reading:
|
158
|
+
|
159
|
+
puts Fmt.read(Float,'0.1') -> 0.1
|
160
|
+
puts Fmt.sep(',').read(Float,'0,1') -> 0.1
|
161
|
+
|
162
|
+
puts Fmt >> [Float, '0.1'] -> 0.1
|
163
|
+
puts Fmt.sep(',') >> [Float, '0,1'] -> 0.1
|
164
|
+
|
165
|
+
===Floating point
|
166
|
+
|
167
|
+
Now let's see something trickier; we will use the floating point result
|
168
|
+
of dividing 2 by 3 and will use a format with 20 fixed digits:
|
169
|
+
|
170
|
+
x = 2.0/3
|
171
|
+
fmt = Fmt.mode(:fix,20)
|
172
|
+
|
173
|
+
puts x.nio_write(fmt) -> 0.66666666666666663
|
174
|
+
|
175
|
+
If you count the digits you will find only 17. Where are the other 3?
|
176
|
+
|
177
|
+
Here we're dealing with an approximate numerical type, Float, that has a limited
|
178
|
+
internal precision and we asked for a higher precision on the output, which we
|
179
|
+
didn't get. Nio refuses to show non-significant digits.
|
180
|
+
|
181
|
+
We can use a placeholder for the digits so that Nio shows us something rather than
|
182
|
+
just ignoring the digits:
|
183
|
+
|
184
|
+
puts x.nio_write(fmt.insignificant_digits('#')) -> 0.66666666666666663###
|
185
|
+
|
186
|
+
Nio is hiding those digits because it assumes that the Float value is an approximation,
|
187
|
+
but we can force it to actually compute the mathematical _exact_ value of the Float:
|
188
|
+
|
189
|
+
puts x.nio_write(fmt.approx_mode(:exact)) -> 0.66666666666666662966
|
190
|
+
|
191
|
+
===Default format
|
192
|
+
|
193
|
+
The default format Fmt.default is used when no format is specified;
|
194
|
+
it can be changed by assigning to it:
|
195
|
+
|
196
|
+
Fmt.default = Fmt.default.sep(',')
|
197
|
+
puts 1.23456.nio_write -> 1,23456
|
198
|
+
puts 1.23456.nio_write(Fmt.prec(3)) -> 1,23
|
199
|
+
|
200
|
+
But note that Fmt.new doesn't use the current default (it's
|
201
|
+
the hard-wired value at which Fmt.default starts):
|
202
|
+
|
203
|
+
puts 1.23456.nio_write(Fmt.new.prec(3)) -> 1.23
|
204
|
+
|
205
|
+
There are also other named prefined formats:
|
206
|
+
|
207
|
+
puts 123456.78.nio_write(Fmt[:dot]) -> 123456.78
|
208
|
+
puts 123456.78.nio_write(Fmt[:dot_th]) -> 123,456.78
|
209
|
+
puts 123456.78.nio_write(Fmt[:comma]) -> 123456,78
|
210
|
+
puts 123456.78.nio_write(Fmt[:code]) -> 123456,78
|
211
|
+
|
212
|
+
The <tt>_th</tt> indicates that thousands separators are used;
|
213
|
+
the :code format is intended for programming languages such as Ruby, C, SQL, etc.
|
214
|
+
These formats can be changed by assigning to them, and also other named formats
|
215
|
+
can be defined:
|
216
|
+
|
217
|
+
Fmt[:code] = Fmt.new.prec(1)
|
218
|
+
puts 123456.78.nio_write(Fmt[:code]) -> 123456.8
|
219
|
+
Fmt[:locale_money] = Fmt.sep(',','.',[3]).prec(:fix,2)
|
220
|
+
puts 123456.78.nio_write(Fmt[:locale_money]) -> 123.456,78
|
221
|
+
|
222
|
+
===Conversions
|
223
|
+
|
224
|
+
Nio can also convert values between numerical types, e.g. from Float to Rational:
|
225
|
+
|
226
|
+
puts Nio.convert(2.0/3, Rational) -> 2/3
|
227
|
+
puts Nio.convert(2.0/3, Rational, :exact) -> 6004799503160661/9007199254740992
|
228
|
+
|
229
|
+
The default (approximate) conversions assumes that the value is inexact and tries to find
|
230
|
+
a nice simple value near it. When we request <tt>:exact</tt> conversion the actual internal value
|
231
|
+
of the floating point number is preserved.
|
232
|
+
|
233
|
+
Let's see some more examples:
|
234
|
+
|
235
|
+
puts Nio.convert(2.0/3, BigDecimal) -> 0.666666666666666666666667E0
|
236
|
+
puts Nio.convert(2.0/3, BigDecimal, :exact) -> 0.66666666666666662965923251249478198587894439697265625E0
|
237
|
+
puts Nio.convert(Rational(2,3), Float) -> 0.666666666666667
|
238
|
+
puts Nio.convert(Rational(2,3), BigDecimal) -> 0.666666666666666666666667E0
|
239
|
+
puts Nio.convert(BigDecimal('2')/3, Rational) -> 2/3
|
240
|
+
puts Nio.convert(BigDecimal('2')/3, Rational, :exact) -> 666666666666666666666667/1000000000000000000000000
|
241
|
+
puts Nio.convert(2.0/3, BigDecimal) -> 0.666666666666666666666667E0
|
242
|
+
puts Nio.convert(2.0/3, BigDecimal, :exact) -> 0.66666666666666662965923251249478198587894439697265625E0
|
243
|
+
puts Nio.convert(BigDecimal('2')/3, Float) -> 0.666666666666667
|
244
|
+
puts Nio.convert(BigDecimal('2')/3, Float, :exact) -> 0.666666666666667
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
=Details
|
249
|
+
|
250
|
+
===Defining formats
|
251
|
+
|
252
|
+
Say you want a numeric format based on the current default but with some aspects
|
253
|
+
changed, e.g. using comma as the decimal separator and with only 3 digits
|
254
|
+
of precision, we could do:
|
255
|
+
|
256
|
+
fmt = Fmt.default
|
257
|
+
fmt = fmt.sep(',')
|
258
|
+
fmt = fmt.prec(3)
|
259
|
+
|
260
|
+
That wasn't very nice. Fmt is a mutable class and have methods to modify
|
261
|
+
its state that end with a bang. We can use them to make this look better,
|
262
|
+
but note that we must create a copy of the default format before we
|
263
|
+
modify it:
|
264
|
+
|
265
|
+
fmt = Fmt.default.dup
|
266
|
+
fmt.sep! ','
|
267
|
+
fmt.prec! 3
|
268
|
+
|
269
|
+
Note that we had to make a duplicate of the default format in
|
270
|
+
order to modify it or we would have got an error
|
271
|
+
(if you want to modify the default format
|
272
|
+
you have to assign to it).
|
273
|
+
|
274
|
+
Now we can simplify this a little by passing a block to Fmt.default:
|
275
|
+
|
276
|
+
fmt = Fmt.default { |f|
|
277
|
+
f.sep! ','
|
278
|
+
f.prec! 3
|
279
|
+
}
|
280
|
+
|
281
|
+
But there's a more concise way to define the format by avoiding
|
282
|
+
the bang-methods and chaining all modifications:
|
283
|
+
|
284
|
+
fmt = Fmt.default.sep(',').prec(3)
|
285
|
+
|
286
|
+
Or even (using shortcut methods such as Fmt.sep or Fmt.prec):
|
287
|
+
|
288
|
+
fmt = Fmt.sep(',').prec(3)
|
289
|
+
|
290
|
+
If we don't want to base the new format on the current default, but use
|
291
|
+
the initial default instead, we would substitute new for default above,
|
292
|
+
except in the last case which always uses the default.
|
293
|
+
For example:
|
294
|
+
|
295
|
+
fmt = Fmt.new { |f|
|
296
|
+
f.sep! ','
|
297
|
+
f.prec! 3
|
298
|
+
}
|
299
|
+
|
300
|
+
fmt = Fmt.new.sep(',').prec(3)
|
301
|
+
|
302
|
+
If a particular case needs a format similar to fmt but with some modification
|
303
|
+
we would use, for example:
|
304
|
+
|
305
|
+
puts 0.1234567.nio_write(fmt.prec(5)) -> 0.12346
|
306
|
+
puts 0.1234567.nio_write(fmt) -> 0.123
|
307
|
+
|
308
|
+
===Exact and aproximate values
|
309
|
+
|
310
|
+
Float and BigDecimal are approximate in the sense that
|
311
|
+
a given value within the range of these types, (defined either
|
312
|
+
by an expression or as result of a computation) may no be
|
313
|
+
exactly represented and has to be substituted by the closest possible value.
|
314
|
+
With Float, which generally is a binary floating point, there's an
|
315
|
+
additional mismatch with the notation (decimal) used for input and
|
316
|
+
output.
|
317
|
+
|
318
|
+
For the following examples we assume that Float is an IEEE754 double precision
|
319
|
+
binary type (i.e. <tt>Float::RADIX==2 && Float::MANT_DIG==53</tt>) which is the
|
320
|
+
common case (in all Ruby platforms I know of, at least).
|
321
|
+
|
322
|
+
0.1.nio_write(Fmt.prec(:exact)) -> 0.1
|
323
|
+
|
324
|
+
Well, that seems pretty clear... but let's complicate things a little:
|
325
|
+
|
326
|
+
0.1.nio_write(Fmt.prec(:exact).show_all_digits) -> 0.10000000000000001
|
327
|
+
|
328
|
+
Mmmm where does that last one came from? Now we're seen a little more exactly what
|
329
|
+
the actual value stored in the Float (the closest Float to 0.1) looks like.
|
330
|
+
|
331
|
+
But why didn't we see the second one-digit in the first try?
|
332
|
+
We requested "exact" precision!
|
333
|
+
|
334
|
+
Well, we didn't get it because it is not needed to specify exactly the inner value
|
335
|
+
of Float(1.0); when we convert 0.1 and round it to the nearest Float we get the
|
336
|
+
same value as when we use 0.10000000000000001.
|
337
|
+
Since we didn't request to see "all digits", we got only as few as possible.
|
338
|
+
|
339
|
+
0.1.nio_write(Fmt.prec(:exact).approx_mode(:exact)) -> 0.1000000000000000055511151231257827021181583404541015625
|
340
|
+
|
341
|
+
Hey! Where did all that stuff came from? Now we're really seeing the "exact" value of Float.
|
342
|
+
(We asked the conversion method to consider the Float an exactly defined value,
|
343
|
+
rather than an approximation to some other value).
|
344
|
+
But, why didn't we get all those digits when we asked for "all digits"?.
|
345
|
+
|
346
|
+
Because most are not significant;
|
347
|
+
the default "approx_mode" is to consider Float an approximate value and show only significant digits.
|
348
|
+
We define insignificant digits as those that can be replaced by any other digit without altering the Float
|
349
|
+
value when the number is rounded to the nearest float. By looking at our example we see that the 17 first digits
|
350
|
+
(just before the 555111...) must be significant: they cannot take an arbitrary value without altering the Float.
|
351
|
+
In fact all have well specified values except the last one that can be either 0 or 1 (but no other value). The next
|
352
|
+
digits (first insignificant, a 5) can be replaced by any other digit * (from 0 to 9) and the expression
|
353
|
+
0.10000000000000000* would still be rounded to Float(0.1)
|
354
|
+
|
355
|
+
|
356
|
+
===Insignificance
|
357
|
+
|
358
|
+
So, let's summarize the situation about inexact numbers: When the approximate mode of a format is <tt>:only_sig</tt>,
|
359
|
+
the digits of inexact (i.e. floating point) numbers are classified as significant or insignificant.
|
360
|
+
The latter are only shown if the property <tt>:all_digits</tt> is true
|
361
|
+
(which it is by default for <tt>:fix</tt>)
|
362
|
+
but since they are not meaningful they use a special character that by default is empty. You can
|
363
|
+
define that character to see where they are:
|
364
|
+
puts 0.1.nio_write(Fmt.mode(:fix,20).insignificant_digits('#')) -> 0.10000000000000001###
|
365
|
+
|
366
|
+
If we hadn't use the special character we would'nt even seen those digits:
|
367
|
+
|
368
|
+
puts 0.1.nio_write(Fmt.mode(:fix,20)) -> 0.10000000000000001
|
369
|
+
|
370
|
+
When the aproximate mode is defined as :exact, there's no distinction between significant or insignificant
|
371
|
+
digits, the number is taken as exactly defined and all digits are shown with their value.
|
372
|
+
|
373
|
+
puts 0.1.nio_write(Fmt.mode(:fix,20,:approx_mode=>:exact) -> 0.10000000000000000555
|
374
|
+
|
375
|
+
===Repeating Numerals
|
376
|
+
|
377
|
+
The common term is <b>repeating decimal</b> or <b>recurring decimal</b>, but since Nio support them for any base,
|
378
|
+
we'll call them <i>repeating numerals</i>.
|
379
|
+
|
380
|
+
Rational(1,3).nio_write -> 0.333...
|
381
|
+
|
382
|
+
We usually find that notation for the decimal expansion of 1/3 in texts, but that doesn't seem very accurate for
|
383
|
+
a conversion library, or is it?
|
384
|
+
|
385
|
+
Rational.nio_read('0.333...') -> Rational(1,3)
|
386
|
+
|
387
|
+
It works the other way! In fact the seemingly loose 0.333... was an exact representation for Nio.
|
388
|
+
|
389
|
+
All Rational numbers can be expressed as a repeating numeral in any base. Repeating numerals may have an infinite
|
390
|
+
number of digits, but from some point on they're just repetitions of the same (finite) sequence of digits.
|
391
|
+
|
392
|
+
By default Nio expresses that kind of repetition by appending two repetitions of the repeating sequence
|
393
|
+
after it and adding the ellipsis (so the repeated sequence appears three times, and, by the way Nio
|
394
|
+
uses three points rather than a real ellipsis characters).
|
395
|
+
This allow Nio to recognize the repeating sequence on input.
|
396
|
+
We can use a more economical notation by just marking the repeating sequence, rather than repeating it:
|
397
|
+
Rational(1,3).nio_write(Fmt.rep(:nreps=>0)) -> 0.<3>
|
398
|
+
We just requested for 0 as the number of repetitions (the default is 2) and got the sequence delimited by <>
|
399
|
+
(we can change those characters; we can even use just a left separator).
|
400
|
+
This is shorter and would allow to show the number better with special typography
|
401
|
+
(e.g. a bar over the repeated digits, a different color, etc.)
|
402
|
+
|
403
|
+
===Rounding
|
404
|
+
|
405
|
+
Rounding is performed on both input and output.
|
406
|
+
When a value is formatted for output the number is rounded to the number of digits
|
407
|
+
that has been specified.
|
408
|
+
|
409
|
+
But also when a value must be read from text rounding it is necessary to choose the nearest
|
410
|
+
numeric representation (e.g. Float value).
|
411
|
+
|
412
|
+
Nio supports three rounding modes which determine how to round _ties_:
|
413
|
+
[<tt>:inf</tt>]
|
414
|
+
round to infinity
|
415
|
+
1.5 -> 2
|
416
|
+
-1.5 -> -2
|
417
|
+
[<tt>:even</tt>] round to even (to the nearest even digit)
|
418
|
+
1.5 -> 2
|
419
|
+
2.5 -> 2
|
420
|
+
-1.5 -> -2
|
421
|
+
-2.5 -> -2
|
422
|
+
[<tt>:zero</tt>] round to zero
|
423
|
+
1.5 -> 1
|
424
|
+
-1.5 -> -1
|
425
|
+
|
426
|
+
Rounding can be set with Nio::Fmt#mode and Nio::Fmt#prec for specific formats, and
|
427
|
+
the default rounding mode can be changed with Fmt.default_rounding_mode().
|
428
|
+
|
429
|
+
For round-trip conversions, a number should use the same rounding mode on input and output.
|
430
|
+
|
431
|
+
For Floats there's an additional issue here, because when we use floating point literals
|
432
|
+
on the Ruby code (such as 0.1 or 1E23) they are parsed and converted to Floating point values
|
433
|
+
by the Ruby interpreter which must apply some kind of rounding when the expression to be parsed
|
434
|
+
is equidistant from two Float values.
|
435
|
+
All the Ruby implementations I have tried have IEEE754 Double Float
|
436
|
+
(<tt>Float::RADIX==2 && Float::MANT_DIG==53</tt>)
|
437
|
+
and floating point literals seem to be rounded according to the round-to-even rule, so that
|
438
|
+
is the initial default rounding mode.
|
439
|
+
|
440
|
+
====Examples
|
441
|
+
|
442
|
+
We assume the common implementation of float (<tt>Float::RADIX==2 && Float::MANT_DIG==53</tt>) here.
|
443
|
+
In that case, we can use the value 1E23, which is equidistant from two Floats
|
444
|
+
to check which kind of roundig does the interpreter use.
|
445
|
+
If it's round-to-even (the common case) we'll have:
|
446
|
+
1E23 == Float.nio_read('1E23',Nio::Fmt.mode(:gen,:exact,:round=>:even) --> true
|
447
|
+
1E23 == Float.nio_read('1E23',Nio::Fmt.mode(:gen,:exact,:round=>:zero) --> true
|
448
|
+
But if rounding is to infinity the previous check will be false and this will hold:
|
449
|
+
1E23 == Float.nio_read('1E23',Nio::Fmt.mode(:gen,:exact,:round=>:inf) --> true
|
450
|
+
(Well, with this example we can't really distinguish :even from :zero, but :zero is most probably not used)
|
451
|
+
|
452
|
+
Now, if we're using the same default rounding for Nio we will have:
|
453
|
+
1E23.nio_write == "1E23" --> true
|
454
|
+
Which will make you feel warm and fuzzy. But if the system rounding is different
|
455
|
+
we will get one of these ugly values:
|
456
|
+
fmt_inf = Nio::Fmt.mode(:gen,:exact,:round=>:inf)
|
457
|
+
fmt_even = Nio::Fmt.mode(:gen,:exact,:round=>:even)
|
458
|
+
Float.nio_read('1E23',fmt_inf).nio_write(fmt_even) -> "1.0000000000000001E23"
|
459
|
+
Float.nio_read('1E23',fmt_even).nio_write(fmt_inf) -> "9.999999999999999E22"
|
460
|
+
|
461
|
+
If the Ruby interpreter doesn't support any of the roundings of Nio, or if it doesn't correctly
|
462
|
+
round, the best solution would be to avoid using Float literals and use Float#nio_read instead.
|
463
|
+
|
464
|
+
===Conversions
|
465
|
+
|
466
|
+
Accurate conversion between numerical types can be performed with Nio.convert.
|
467
|
+
It takes three arguments: the value to convert, the class to convert it to (Float, BigDecimal,
|
468
|
+
Flt::Num, Integer or Rational) and the conversion mode, either :exact, which is..., well, quite exact,
|
469
|
+
and :approx which tries to find a simpler value (e.g. 0.1 rather than 0.10000000000000001...)
|
470
|
+
within the accuray of the original value.
|
471
|
+
The :approx mode may be pretty slow in some cases.
|
472
|
+
|
473
|
+
Nio.convert(0.1,BigDecimal,:exact) -> 0.1000000000 0000000555 1115123125 ...
|
474
|
+
Nio.convert(0.1,BigDecimal,:approx) -> 0.1
|
475
|
+
|
476
|
+
We can check out the accuracy of the conversions:
|
477
|
+
|
478
|
+
Nio.convert(BigDecimal('1.234567890123456'),Float)==1.234567890123456 -> true
|
479
|
+
Nio.convert(BigDecimal('355')/226,Float)==(355.0/226) -> true
|
480
|
+
|
481
|
+
Thay may not look very impressive, but is much more accurate than BigDecimal#to_f
|
482
|
+
(at least in Ruby versions up to 1.8.6, mswin32 (specially) and linux) for which:
|
483
|
+
|
484
|
+
BigDecimal('1.234567890123456').to_f == 1.234567890123456 -> false
|
485
|
+
(BigDecimal('355')/226).to_f == (355.0/226.0) -> false
|
486
|
+
|
487
|
+
=License
|
488
|
+
|
489
|
+
This code is free to use under the terms of the GNU GENERAL PUBLIC LICENSE.
|
490
|
+
|
491
|
+
=Contact
|
492
|
+
|
493
|
+
Nio has been developed by Javier Goizueta (mailto:javier@goizueta.info).
|
494
|
+
|
495
|
+
You can contact me through Rubyforge:http://rubyforge.org/sendmessage.php?touser=25432
|
496
|
+
|
497
|
+
|
498
|
+
=More Information
|
499
|
+
|
500
|
+
* <b>What Every Computer Scientist Should Know About Floating-Point Arithmetic</b>
|
501
|
+
David Goldberg
|
502
|
+
- http://docs.sun.com/source/806-3568/ncg_goldberg.html
|
503
|
+
|
504
|
+
* <b>How to Read Floating Point Numbers Accurately</b>
|
505
|
+
William D. Clinger
|
506
|
+
- http://citeseer.ist.psu.edu/224562.html
|
507
|
+
|
508
|
+
* <b>Printing Floating-Point Numbers Quickly and Accurately</b>
|
509
|
+
Robert G. Burger & R. Kent Dybvig
|
510
|
+
- http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
|
511
|
+
|
512
|
+
* <b>Repeating Decimal</b>
|
513
|
+
- http://mathworld.wolfram.com/RepeatingDecimal.html
|
514
|
+
- http://en.wikipedia.org/wiki/Recurring_decimal
|
515
|
+
|
516
|
+
* For <b>floating point rationalization algorithms</b>, see my commented
|
517
|
+
source code for the <tt>rntlzr</tt> module from Nio,
|
518
|
+
which you can download in PDF here:
|
519
|
+
- http://perso.wanadoo.es/jgoizueta/dev/goi/rtnlzr.pdf
|