1require_relative '../../spec_helper'
2require_relative 'fixtures/classes'
3
4describe :kernel_float, shared: true do
5  it "returns the identical Float for numeric Floats" do
6    float = 1.12
7    float2 = @object.send(:Float, float)
8    float2.should == float
9    float2.should equal float
10  end
11
12  it "returns a Float for Fixnums" do
13    @object.send(:Float, 1).should == 1.0
14  end
15
16  it "returns a Float for Complex with only a real part" do
17    @object.send(:Float, Complex(1)).should == 1.0
18  end
19
20  it "returns a Float for Bignums" do
21    @object.send(:Float, 1000000000000).should == 1000000000000.0
22  end
23
24  it "raises an ArgumentError for nil" do
25    lambda { @object.send(:Float, nil) }.should raise_error(TypeError)
26  end
27
28  it "returns the identical NaN for NaN" do
29    nan = nan_value
30    nan.nan?.should be_true
31    nan2 = @object.send(:Float, nan)
32    nan2.nan?.should be_true
33    nan2.should equal(nan)
34  end
35
36  it "returns the same Infinity for Infinity" do
37    infinity = infinity_value
38    infinity2 = @object.send(:Float, infinity)
39    infinity2.should == infinity_value
40    infinity.should equal(infinity2)
41  end
42
43  it "converts Strings to floats without calling #to_f" do
44    string = "10"
45    string.should_not_receive(:to_f)
46    @object.send(:Float, string).should == 10.0
47  end
48
49  it "converts Strings with decimal points into Floats" do
50    @object.send(:Float, "10.0").should == 10.0
51  end
52
53  it "raises an ArgumentError for a String of word characters" do
54    lambda { @object.send(:Float, "float") }.should raise_error(ArgumentError)
55  end
56
57  it "raises an ArgumentError if there are two decimal points in the String" do
58    lambda { @object.send(:Float, "10.0.0") }.should raise_error(ArgumentError)
59  end
60
61  it "raises an ArgumentError for a String of numbers followed by word characters" do
62    lambda { @object.send(:Float, "10D") }.should raise_error(ArgumentError)
63  end
64
65  it "raises an ArgumentError for a String of word characters followed by numbers" do
66    lambda { @object.send(:Float, "D10") }.should raise_error(ArgumentError)
67  end
68
69  it "is strict about the string form even across newlines" do
70    lambda { @object.send(:Float, "not a number\n10") }.should raise_error(ArgumentError)
71    lambda { @object.send(:Float, "10\nnot a number") }.should raise_error(ArgumentError)
72  end
73
74  it "converts String subclasses to floats without calling #to_f" do
75    my_string = Class.new(String) do
76      def to_f() 1.2 end
77    end
78
79    @object.send(:Float, my_string.new("10")).should == 10.0
80  end
81
82  it "returns a positive Float if the string is prefixed with +" do
83    @object.send(:Float, "+10").should == 10.0
84    @object.send(:Float, " +10").should == 10.0
85  end
86
87  it "returns a negative Float if the string is prefixed with +" do
88    @object.send(:Float, "-10").should == -10.0
89    @object.send(:Float, " -10").should == -10.0
90  end
91
92  it "raises an ArgumentError if a + or - is embedded in a String" do
93    lambda { @object.send(:Float, "1+1") }.should raise_error(ArgumentError)
94    lambda { @object.send(:Float, "1-1") }.should raise_error(ArgumentError)
95  end
96
97  it "raises an ArgumentError if a String has a trailing + or -" do
98    lambda { @object.send(:Float, "11+") }.should raise_error(ArgumentError)
99    lambda { @object.send(:Float, "11-") }.should raise_error(ArgumentError)
100  end
101
102  it "raises an ArgumentError for a String with a leading _" do
103    lambda { @object.send(:Float, "_1") }.should raise_error(ArgumentError)
104  end
105
106  it "returns a value for a String with an embedded _" do
107    @object.send(:Float, "1_000").should == 1000.0
108  end
109
110  it "raises an ArgumentError for a String with a trailing _" do
111    lambda { @object.send(:Float, "10_") }.should raise_error(ArgumentError)
112  end
113
114  it "raises an ArgumentError for a String of \\0" do
115    lambda { @object.send(:Float, "\0") }.should raise_error(ArgumentError)
116  end
117
118  it "raises an ArgumentError for a String with a leading \\0" do
119    lambda { @object.send(:Float, "\01") }.should raise_error(ArgumentError)
120  end
121
122  it "raises an ArgumentError for a String with an embedded \\0" do
123    lambda { @object.send(:Float, "1\01") }.should raise_error(ArgumentError)
124  end
125
126  it "raises an ArgumentError for a String with a trailing \\0" do
127    lambda { @object.send(:Float, "1\0") }.should raise_error(ArgumentError)
128  end
129
130  it "raises an ArgumentError for a String that is just an empty space" do
131    lambda { @object.send(:Float, " ") }.should raise_error(ArgumentError)
132  end
133
134  it "raises an ArgumentError for a String that with an embedded space" do
135    lambda { @object.send(:Float, "1 2") }.should raise_error(ArgumentError)
136  end
137
138  it "returns a value for a String with a leading space" do
139    @object.send(:Float, " 1").should == 1.0
140  end
141
142  it "returns a value for a String with a trailing space" do
143    @object.send(:Float, "1 ").should == 1.0
144  end
145
146  it "returns a value for a String with any leading whitespace" do
147    @object.send(:Float, "\t\n1").should == 1.0
148  end
149
150  it "returns a value for a String with any trailing whitespace" do
151    @object.send(:Float, "1\t\n").should == 1.0
152  end
153
154  %w(e E).each do |e|
155    it "raises an ArgumentError if #{e} is the trailing character" do
156      lambda { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError)
157    end
158
159    it "raises an ArgumentError if #{e} is the leading character" do
160      lambda { @object.send(:Float, "#{e}2") }.should raise_error(ArgumentError)
161    end
162
163    it "returns Infinity for '2#{e}1000'" do
164      @object.send(:Float, "2#{e}1000").should == Float::INFINITY
165    end
166
167    it "returns 0 for '2#{e}-1000'" do
168      @object.send(:Float, "2#{e}-1000").should == 0
169    end
170
171    it "allows embedded _ in a number on either side of the #{e}" do
172      @object.send(:Float, "2_0#{e}100").should == 20e100
173      @object.send(:Float, "20#{e}1_00").should == 20e100
174      @object.send(:Float, "2_0#{e}1_00").should == 20e100
175    end
176
177    it "raises an exception if a space is embedded on either side of the '#{e}'" do
178      lambda { @object.send(:Float, "2 0#{e}100") }.should raise_error(ArgumentError)
179      lambda { @object.send(:Float, "20#{e}1 00") }.should raise_error(ArgumentError)
180    end
181
182    it "raises an exception if there's a leading _ on either side of the '#{e}'" do
183      lambda { @object.send(:Float, "_20#{e}100") }.should raise_error(ArgumentError)
184      lambda { @object.send(:Float, "20#{e}_100") }.should raise_error(ArgumentError)
185    end
186
187    it "raises an exception if there's a trailing _ on either side of the '#{e}'" do
188      lambda { @object.send(:Float, "20_#{e}100") }.should raise_error(ArgumentError)
189      lambda { @object.send(:Float, "20#{e}100_") }.should raise_error(ArgumentError)
190    end
191
192    it "allows decimal points on the left side of the '#{e}'" do
193      @object.send(:Float, "2.0#{e}2").should == 2e2
194    end
195
196    it "raises an ArgumentError if there's a decimal point on the right side of the '#{e}'" do
197      lambda { @object.send(:Float, "20#{e}2.0") }.should raise_error(ArgumentError)
198    end
199  end
200
201  describe "for hexadecimal literals with binary exponent" do
202    %w(p P).each do |p|
203      it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do
204        @object.send(:Float, "0x10#{p}0").should == 16.0
205      end
206
207      it "interprets the exponent (on the right of '#{p}') in decimal" do
208        @object.send(:Float, "0x1#{p}10").should == 1024.0
209      end
210
211      it "raises an ArgumentError if #{p} is the trailing character" do
212        lambda { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError)
213      end
214
215      it "raises an ArgumentError if #{p} is the leading character" do
216        lambda { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError)
217      end
218
219      it "returns Infinity for '0x1#{p}10000'" do
220        @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY
221      end
222
223      it "returns 0 for '0x1#{p}-10000'" do
224        @object.send(:Float, "0x1#{p}-10000").should == 0
225      end
226
227      it "allows embedded _ in a number on either side of the #{p}" do
228        @object.send(:Float, "0x1_0#{p}10").should == 16384.0
229        @object.send(:Float, "0x10#{p}1_0").should == 16384.0
230        @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0
231      end
232
233      it "raises an exception if a space is embedded on either side of the '#{p}'" do
234        lambda { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError)
235        lambda { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError)
236      end
237
238      it "raises an exception if there's a leading _ on either side of the '#{p}'" do
239        lambda { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError)
240        lambda { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError)
241      end
242
243      it "raises an exception if there's a trailing _ on either side of the '#{p}'" do
244        lambda { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError)
245        lambda { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError)
246      end
247
248      it "allows hexadecimal points on the left side of the '#{p}'" do
249        @object.send(:Float, "0x1.8#{p}0").should == 1.5
250      end
251
252      it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do
253        lambda { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError)
254      end
255    end
256  end
257
258  it "returns a Float that can be a parameter to #Float again" do
259    float = @object.send(:Float, "10")
260    @object.send(:Float, float).should == 10.0
261  end
262
263  it "otherwise, converts the given argument to a Float by calling #to_f" do
264    (obj = mock('1.2')).should_receive(:to_f).once.and_return(1.2)
265    obj.should_not_receive(:to_i)
266    @object.send(:Float, obj).should == 1.2
267  end
268
269  it "returns the identical NaN if to_f is called and it returns NaN" do
270    nan = nan_value
271    (nan_to_f = mock('NaN')).should_receive(:to_f).once.and_return(nan)
272    nan2 = @object.send(:Float, nan_to_f)
273    nan2.nan?.should be_true
274    nan2.should equal(nan)
275  end
276
277  it "returns the identical Infinity if to_f is called and it returns Infinity" do
278    infinity = infinity_value
279    (infinity_to_f = mock('Infinity')).should_receive(:to_f).once.and_return(infinity)
280    infinity2 = @object.send(:Float, infinity_to_f)
281    infinity2.should equal(infinity)
282  end
283
284  it "raises a TypeError if #to_f is not provided" do
285    lambda { @object.send(:Float, mock('x')) }.should raise_error(TypeError)
286  end
287
288  it "raises a TypeError if #to_f returns a String" do
289    (obj = mock('ha!')).should_receive(:to_f).once.and_return('ha!')
290    lambda { @object.send(:Float, obj) }.should raise_error(TypeError)
291  end
292
293  it "raises a TypeError if #to_f returns an Integer" do
294    (obj = mock('123')).should_receive(:to_f).once.and_return(123)
295    lambda { @object.send(:Float, obj) }.should raise_error(TypeError)
296  end
297
298  it "raises a RangeError when passed a Complex argument" do
299    c = Complex(2, 3)
300    lambda { @object.send(:Float, c) }.should raise_error(RangeError)
301  end
302end
303
304describe "Kernel.Float" do
305  it_behaves_like :kernel_float, :Float, Kernel
306end
307
308describe "Kernel#Float" do
309  it_behaves_like :kernel_float, :Float, Object.new
310end
311
312describe "Kernel#Float" do
313  it "is a private method" do
314    Kernel.should have_private_instance_method(:Float)
315  end
316end
317