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