nio 0.2.3 → 0.2.4

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