nio 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.2.0 2007-09-22
2
+
3
+ * Initial release
4
+ - The Nio library has been extracted from and old project
5
+ and packages as a Gem.
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Javier Goizueta
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,34 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ SOURCE.txt
6
+ Rakefile
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/nio.rb
10
+ lib/nio/version.rb
11
+ lib/nio/repdec.rb
12
+ lib/nio/flttol.rb
13
+ lib/nio/tools.rb
14
+ lib/nio/rtnlzr.rb
15
+ lib/nio/fmt.rb
16
+ lib/nio/sugar.rb
17
+ log/debug.log
18
+ script/destroy
19
+ script/destroy.cmd
20
+ script/generate
21
+ script/generate.cmd
22
+ script/txt2html
23
+ script/txt2html.cmd
24
+ setup.rb
25
+ tasks/nuweb.rake
26
+ tasks/deployment.rake
27
+ tasks/environment.rake
28
+ tasks/website.rake
29
+ test/data.yaml
30
+ test/test_helper.rb
31
+ test/test_tools.rb
32
+ test/test_repdec.rb
33
+ test/test_rtnlzr.rb
34
+ test/test_fmt.rb
data/README.txt ADDED
@@ -0,0 +1,560 @@
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
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/SOURCE.txt ADDED
@@ -0,0 +1,31 @@
1
+ =Notes on the source code
2
+
3
+ The Ruby code in this project is generated from the source code
4
+ in the directory "source" by the Rake task *tangle*.
5
+
6
+ You can get the updated source code from the SVN repository
7
+ at Rubyforge (svn://rubyforge.org/var/svn/nio/trunk)
8
+ or from the "nio-source" package releases from the project.
9
+
10
+ The nuweb source files have extension .w
11
+
12
+ There's also a rake task named *weave* which generates
13
+ a documented form of the source code in PDF format,
14
+ which is saved to the directory source/pdf.
15
+
16
+ This tasks requires the program nuweb which converts
17
+ the nuweb sources (files with extension .w) in
18
+ directory source to either ruby files or
19
+ LaTeX files for the documentated source.
20
+ For the weave task latex and dvipdfm are also necessary.
21
+
22
+ You can get the PDF files corresponding to each realese
23
+ in the "nio-source-pdf" packages.
24
+
25
+ The necessary nuweb tool is available here for windows:
26
+ http://perso.wanadoo.es/jgoizueta/dev/tools/nuweb-win32.zip
27
+
28
+ For other systems the source code and an necessary patch
29
+ can be obtained here:
30
+ http://www.goizueta.info/javier/index.html?pg=dev/req.en.html
31
+