float-formats 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,273 +1,145 @@
1
- #Float-Formats -- Native Ruby Float support tools
2
- #
3
- # Float::RADIX : b = Radix of exponent representation,2
4
- #
5
- # Float::MANT_DIG : p = bits (base-RADIX digits) in the significand
6
- #
7
- #
8
- # Float::DIG : q = Number of decimal digits such that any floating-point number with q
9
- # decimal digits can be rounded into a floating-point number with p radix b
10
- # digits and back again without change to the q decimal digits,
11
- # q = p * log10(b) if b is a power of 10
12
- # q = floor((p - 1) * log10(b)) otherwise
13
- #
14
- # Float::MIN_EXP : emin = Minimum int x such that Float::RADIX**(x-1) is a normalized float
15
- #
16
- # Float::MIN_10_EXP : Minimum negative integer such that 10 raised to that power is in the
17
- # range of normalized floating-point numbers,
18
- # ceil(log10(b) * (emin - 1))
19
- #
20
- # Float::MAX_EXP : emax = Maximum int x such that Float::RADIX**(x-1) is a representable float
21
- #
22
- # Float::MAX_10_EXP : Maximum integer such that 10 raised to that power is in the range of
23
- # representable finite floating-point numbers,
24
- # floor(log10((1 - b**-p) * b**emax))
25
- #
26
- # Float::MAX : Maximum representable finite floating-point number
27
- # (1 - b**-p) * b**emax
28
- #
29
- # Float::EPSILON : The difference between 1 and the least value greater than 1 that is
30
- # representable in the given floating point type
31
- # b**(1-p)
32
- #
33
- # Float::MIN : Minimum normalized positive floating-point number
34
- # b**(emin - 1).
35
- #
36
- # Float::ROUNDS : Addition rounds to 0: zero, 1: nearest, 2: +inf, 3: -inf, -1: unknown.
37
- #
38
- # defined by Nio:
39
- # Float::DECIMAL_DIG : Number of decimal digits, n, such that any floating-point number can be rounded
40
- # to a floating-point number with n decimal digits and back again without
41
- # change to the value,
42
- # pmax * log10(b) if b is a power of 10
43
- # ceil(1 + pmax * log10(b)) otherwise
44
- #
45
- # Note that this uses the "fractional significand" interpretation
46
-
47
- require 'nio'
48
- require 'nio/sugar'
49
-
50
- require 'float-formats/bytes.rb'
51
-
52
- class Float
53
- # Compute the adjacent floating point values: largest value not larger that
54
- # this and smallest not smaller.
55
- def neighbours
56
- f,e = Math.frexp(self)
57
- e = Float::MIN_EXP if f==0
58
- e = [Float::MIN_EXP,e].max
59
- dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
60
-
61
- min_f = 0.5 #0.5==Math.ldexp(2**(bits-1),-Float::MANT_DIG)
62
- max_f = 1.0 - Math.ldexp(1,-Float::MANT_DIG)
63
-
64
- if f==max_f
65
- high = self + dx*2
66
- elsif f==-min_f && e!=Float::MIN_EXP
67
- high = self + dx/2
68
- else
69
- high = self + dx
70
- end
71
- if e==Float::MIN_EXP || f!=min_f
72
- low = self - dx
73
- elsif f==-max_f
74
- high = self - dx*2
75
- else
76
- low = self - dx/2
77
- end
78
- [low, high]
79
- end
80
-
81
- # Previous floating point value
82
- def prev
83
- neighbours[0]
84
- end
85
-
86
- # Next floating point value; e.g. 1.0.next == 1.0+Float::EPSILON
87
- def next
88
- neighbours[1]
89
- end
90
-
91
- # Minimum normalized number == MAX_D.next
92
- MIN_N = Math.ldexp(0.5,Float::MIN_EXP) # == nxt(MAX_D) == Float::MIN
93
-
94
- # Maximum denormal number == MIN_N.prev
95
- MAX_D = Math.ldexp(Math.ldexp(1,Float::MANT_DIG-1)-1,Float::MIN_EXP-Float::MANT_DIG)
96
-
97
- # Minimum non zero positive denormal number == 0.0.next
98
- MIN_D = Math.ldexp(1,Float::MIN_EXP-Float::MANT_DIG);
99
-
100
- # Maximum significand == Math.ldexp(Math.ldexp(1,Float::MANT_DIG)-1,-Float::MANT_DIG)
101
- MAX_F = Math.frexp(Float::MAX)[0] == Math.ldexp(Math.ldexp(1,Float::MANT_DIG)-1,-Float::MANT_DIG)
102
-
103
- # ulp (unit in the last place) according to the definition proposed by J.M. Muller in
104
- # "On the definition of ulp(x)" INRIA No. 5504
105
- def ulp
106
- return self if nan?
107
- x = abs
108
- if x < Math.ldexp(1,MIN_EXP) # x < RADIX*MIN_N
109
- res = Math.ldexp(1,MIN_EXP-MANT_DIG) # res = MIN_D
110
- elsif x > Math.ldexp(1-Math.ldexp(1,-MANT_DIG),MAX_EXP) # x > MAX
111
- res = Math.ldexp(1,MAX_EXP-MANT_DIG) # res = MAX - MAX.prev
112
- else
113
- f,e = Math.frexp(x)
114
- if f==Math.ldexp(1,-1)
115
- res = Math.ldexp(1,e-MANT_DIG-1)
116
- else
117
- res = Math.ldexp(1,e-MANT_DIG)
118
- end
119
- end
120
- res
121
- end
122
-
123
-
124
- end
125
-
126
-
127
- module FltPnt
128
-
129
- module_function
130
-
131
- # shortest decimal unambiguous reprentation
132
- def float_shortest_dec(x)
133
- x.nio_write(Nio::Fmt.prec(:exact))
134
- end
135
-
136
- # decimal representation showing all significant digits
137
- def float_significant_dec(x)
138
- x.nio_write(Nio::Fmt.prec(:exact).show_all_digits(true))
139
- end
140
-
141
- # complete exact decimal representation
142
- def float_dec(x)
143
- x.nio_write(Nio::Fmt.prec(:exact).approx_mode(:exact))
144
- end
145
-
146
- # binary representation
147
- def float_bin(x)
148
- x.nio_write(Nio::Fmt.mode(:sci,:exact).base(2))
149
- end
150
-
151
- # decompose a float into a signed integer significand and exponent (base Float::RADIX)
152
- def float_to_integral_significand_exponent(x)
153
- s,e = Math.frexp(x)
154
- [Math.ldexp(s,Float::MANT_DIG).to_i,e-Float::MANT_DIG]
155
- end
156
-
157
- # compose float from significand and exponent
158
- def float_from_integral_significand_exponent(s,e)
159
- Math.ldexp(s,e)
160
- end
161
-
162
- def float_to_integral_sign_significand_exponent(x)
163
- if x==0.0
164
- sign = (1/x<0) ? -1 : +1
165
- else
166
- sign = x<0 ? -1 : +1
167
- end
168
- x = -x if sign<0
169
- s,e = Math.frexp(x)
170
- [sign,Math.ldexp(s,Float::MANT_DIG).to_i,e-Float::MANT_DIG]
171
- end
172
-
173
- def float_from_integral_sign_significand_exponent(sgn,s,e)
174
- f = Math.ldexp(s,e)
175
- f = -f if sgn<0
176
- f
177
- end
178
-
179
- # convert a float to C99's hexadecimal notation
180
- def hex_from_float(v)
181
- if Float::RADIX==2
182
- sgn,s,e = float_to_integral_sign_significand_exponent(v)
183
- else
184
- txt = v.nio_write(Fmt.base(2).sep('.')).upcase
185
- p = txt.index('E')
186
- exp = 0
187
- if p
188
- exp = rep[p+1..-1].to_i
189
- txt = rep[0...p]
190
- end
191
- p = txt.index('.')
192
- if p
193
- exp -= (txt.size-p-1)
194
- txt.tr!('.','')
195
- end
196
- s = txt.to_i(2)
197
- e = exp
198
- end
199
- "0x#{sgn<0 ? '-' : ''}#{s.to_s(16)}p#{e}"
200
- end
201
-
202
- # convert a string formatted in C99's hexadecimal notation to a float
203
- def hex_to_float(txt)
204
- txt = txt.strip.upcase
205
- txt = txt[2..-1] if txt[0,2]=='0X'
206
- p = txt.index('P')
207
- if p
208
- exp = txt[p+1..-1].to_i
209
- txt = txt[0...p]
210
- else
211
- exp = 0
212
- end
213
- p = txt.index('.')
214
- if p
215
- exp -= (txt.size-p-1)*4
216
- txt.tr!('.','')
217
- end
218
- if Float::RADIX==2
219
- v = txt.to_i(16)
220
- if v==0 && txt.include?('-')
221
- sign = -1
222
- elsif v<0
223
- sign = -1
224
- v = -v
225
- else
226
- sign = +1
227
- end
228
- float_from_integral_sign_significand_exponent(sign,v,exp)
229
- else
230
- (txt.to_i(16)*(2**exp)).to_f
231
- end
232
- end
233
-
234
- # ===== IEEE types =====================================================================================
235
-
236
- # generate a SGL value stored in a byte string given a decimal value formatted as text
237
- def sgl_from_text(txt, little_endian=true)
238
- code = little_endian ? 'e':'g'
239
- [txt].pack(code)
240
- end
241
-
242
- # generate a SGL value stored in a byte string given a Float value
243
- def sgl_from_float(val, little_endian=true)
244
- code = little_endian ? 'e':'g'
245
- [val].pack(code)
246
- end
247
-
248
- # convert a SGL value stored in a byte string to a Float value
249
- def sgl_to_float(sgl, littel_endian=true)
250
- code = little_endian ? 'e':'g'
251
- sgl.unpack(code)[0]
252
- end
253
-
254
-
255
- # generate a DBL value stored in a byte string given a decimal value formatted as text
256
- def dbl_from_text(txt, little_endian=true)
257
- code = little_endian ? 'E':'G'
258
- [txt].pack(code)
259
- end
260
-
261
- # generate a DBL value stored in a byte string given a Float value
262
- def dbl_from_float(val, little_endian=true)
263
- code = little_endian ? 'E':'G'
264
- [val].pack(code)
265
- end
266
-
267
- # convert a DBL value stored in a byte string to a Float value
268
- def dbl_to_float(sgl, little_endian=true)
269
- code = little_endian ? 'E':'G'
270
- sgl.unpack(code)[0]
271
- end
272
-
1
+ #Float-Formats -- Native Ruby Float support tools
2
+
3
+ require 'nio'
4
+ require 'nio/sugar'
5
+ require 'flt'
6
+ require 'flt/float'
7
+ require 'float-formats/bytes'
8
+
9
+ module Flt
10
+
11
+ module_function
12
+
13
+ # shortest decimal unambiguous reprentation
14
+ def float_shortest_dec(x)
15
+ x.nio_write(Nio::Fmt.prec(:exact))
16
+ end
17
+
18
+ # decimal representation showing all significant digits
19
+ def float_significant_dec(x)
20
+ x.nio_write(Nio::Fmt.prec(:exact).show_all_digits(true))
21
+ end
22
+
23
+ # complete exact decimal representation
24
+ def float_dec(x)
25
+ x.nio_write(Nio::Fmt.prec(:exact).approx_mode(:exact))
26
+ end
27
+
28
+ # binary representation
29
+ def float_bin(x)
30
+ x.nio_write(Nio::Fmt.mode(:sci,:exact).base(2))
31
+ end
32
+
33
+ # decompose a float into a signed integer significand and exponent (base Float::RADIX)
34
+ def float_to_integral_significand_exponent(x)
35
+ Float.context.to_int_scale(x)
36
+ end
37
+
38
+ # compose float from significand and exponent
39
+ def float_from_integral_significand_exponent(s,e)
40
+ Float.context.Num(s,e)
41
+ end
42
+
43
+ def float_to_integral_sign_significand_exponent(x)
44
+ Float.context.split(x)
45
+ end
46
+
47
+ def float_from_integral_sign_significand_exponent(sgn,s,e)
48
+ Float.context.Num(sgn,s,e)
49
+ end
50
+
51
+ # convert a float to C99's hexadecimal notation
52
+ def hex_from_float(v)
53
+ if Float::RADIX==2
54
+ sgn,s,e = float_to_integral_sign_significand_exponent(v)
55
+ else
56
+ txt = v.nio_write(Fmt.base(2).sep('.')).upcase
57
+ p = txt.index('E')
58
+ exp = 0
59
+ if p
60
+ exp = rep[p+1..-1].to_i
61
+ txt = rep[0...p]
62
+ end
63
+ p = txt.index('.')
64
+ if p
65
+ exp -= (txt.size-p-1)
66
+ txt.tr!('.','')
67
+ end
68
+ s = txt.to_i(2)
69
+ e = exp
70
+ end
71
+ "0x#{sgn<0 ? '-' : ''}#{s.to_s(16)}p#{e}"
72
+ end
73
+
74
+ # convert a string formatted in C99's hexadecimal notation to a float
75
+ def hex_to_float(txt)
76
+ txt = txt.strip.upcase
77
+ txt = txt[2..-1] if txt[0,2]=='0X'
78
+ p = txt.index('P')
79
+ if p
80
+ exp = txt[p+1..-1].to_i
81
+ txt = txt[0...p]
82
+ else
83
+ exp = 0
84
+ end
85
+ p = txt.index('.')
86
+ if p
87
+ exp -= (txt.size-p-1)*4
88
+ txt.tr!('.','')
89
+ end
90
+ if Float::RADIX==2
91
+ v = txt.to_i(16)
92
+ if v==0 && txt.include?('-')
93
+ sign = -1
94
+ elsif v<0
95
+ sign = -1
96
+ v = -v
97
+ else
98
+ sign = +1
99
+ end
100
+ float_from_integral_sign_significand_exponent(sign,v,exp)
101
+ else
102
+ (txt.to_i(16)*(2**exp)).to_f
103
+ end
104
+ end
105
+
106
+ # ===== IEEE types =====================================================================================
107
+
108
+ # generate a SGL value stored in a byte string given a decimal value formatted as text
109
+ def sgl_from_text(txt, little_endian=true)
110
+ code = little_endian ? 'e':'g'
111
+ [txt].pack(code)
112
+ end
113
+
114
+ # generate a SGL value stored in a byte string given a Float value
115
+ def sgl_from_float(val, little_endian=true)
116
+ code = little_endian ? 'e':'g'
117
+ [val].pack(code)
118
+ end
119
+
120
+ # convert a SGL value stored in a byte string to a Float value
121
+ def sgl_to_float(sgl, littel_endian=true)
122
+ code = little_endian ? 'e':'g'
123
+ sgl.unpack(code)[0]
124
+ end
125
+
126
+
127
+ # generate a DBL value stored in a byte string given a decimal value formatted as text
128
+ def dbl_from_text(txt, little_endian=true)
129
+ code = little_endian ? 'E':'G'
130
+ [txt].pack(code)
131
+ end
132
+
133
+ # generate a DBL value stored in a byte string given a Float value
134
+ def dbl_from_float(val, little_endian=true)
135
+ code = little_endian ? 'E':'G'
136
+ [val].pack(code)
137
+ end
138
+
139
+ # convert a DBL value stored in a byte string to a Float value
140
+ def dbl_to_float(sgl, little_endian=true)
141
+ code = little_endian ? 'E':'G'
142
+ sgl.unpack(code)[0]
143
+ end
144
+
273
145
  end
@@ -1,8 +1,8 @@
1
- module FltPnt
2
- module VERSION #:nodoc:
1
+ module Flt
2
+ module FORMATS_VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 1
5
- TINY = 1
4
+ MINOR = 2
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/tasks/ann.rake ADDED
@@ -0,0 +1,80 @@
1
+
2
+ begin
3
+ require 'bones/smtp_tls'
4
+ rescue LoadError
5
+ require 'net/smtp'
6
+ end
7
+ require 'time'
8
+
9
+ namespace :ann do
10
+
11
+ # A prerequisites task that all other tasks depend upon
12
+ task :prereqs
13
+
14
+ file PROJ.ann.file do
15
+ ann = PROJ.ann
16
+ puts "Generating #{ann.file}"
17
+ File.open(ann.file,'w') do |fd|
18
+ fd.puts("#{PROJ.name} version #{PROJ.version}")
19
+ fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
20
+ fd.puts(" #{PROJ.url}") if PROJ.url.valid?
21
+ fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
22
+ fd.puts
23
+ fd.puts("== DESCRIPTION")
24
+ fd.puts
25
+ fd.puts(PROJ.description)
26
+ fd.puts
27
+ fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
28
+ fd.puts
29
+ ann.paragraphs.each do |p|
30
+ fd.puts "== #{p.upcase}"
31
+ fd.puts
32
+ fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
33
+ fd.puts
34
+ end
35
+ fd.puts ann.text if ann.text
36
+ end
37
+ end
38
+
39
+ desc "Create an announcement file"
40
+ task :announcement => ['ann:prereqs', PROJ.ann.file]
41
+
42
+ desc "Send an email announcement"
43
+ task :email => ['ann:prereqs', PROJ.ann.file] do
44
+ ann = PROJ.ann
45
+ from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
46
+ to = Array(ann.email[:to])
47
+
48
+ ### build a mail header for RFC 822
49
+ rfc822msg = "From: #{from}\n"
50
+ rfc822msg << "To: #{to.join(',')}\n"
51
+ rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
52
+ rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
53
+ rfc822msg << "\n"
54
+ rfc822msg << "Date: #{Time.new.rfc822}\n"
55
+ rfc822msg << "Message-Id: "
56
+ rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
57
+ rfc822msg << File.read(ann.file)
58
+
59
+ params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
60
+ ann.email[key]
61
+ end
62
+
63
+ params[3] = PROJ.email if params[3].nil?
64
+
65
+ if params[4].nil?
66
+ STDOUT.write "Please enter your e-mail password (#{params[3]}): "
67
+ params[4] = STDIN.gets.chomp
68
+ end
69
+
70
+ ### send email
71
+ Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
72
+ end
73
+ end # namespace :ann
74
+
75
+ desc 'Alias to ann:announcement'
76
+ task :ann => 'ann:announcement'
77
+
78
+ CLOBBER << PROJ.ann.file
79
+
80
+ # EOF