1# -*- mode: perl; -*-
2
3# Binary, octal, and hexadecimal floating point literals were introduced in
4# v5.22.0.
5#
6# - It wasn't until v5.28.0 that binary, octal, and hexadecimal floating point
7#   literals were converted to the correct value on perls compiled with quadmath
8#   support.
9#
10# - It wasn't until v5.32.0 that binary and octal floating point literals worked
11#   correctly with constant overloading. Before v5.32.0, it seems like the
12#   second character is always silently converted to an "x", so, e.g., "0b1.1p8"
13#   is passed to the overload::constant subroutine as "0x1.1p8", and "01.1p+8"
14#   is passed as "0x.1p+8".
15#
16# - Octal floating point literals using the "0o" prefix were introduced in
17#   v5.34.0.
18
19# Note that all numeric literals that should not be overloaded must be quoted.
20
21use strict;
22use warnings;
23
24use Test::More tests => "171";
25
26use bigfloat;
27
28my $class = "Math::BigFloat";
29my $x;
30
31################################################################################
32# The following tests should be identical for Math::BigInt, Math::BigFloat and
33# Math::BigRat.
34
35# These are handled by "binary".
36
37$x = 0xff;
38is($x, "255", "hexadecimal integer literal 0xff");
39is(ref($x), "Math::BigFloat",
40     "value is a Math::BigInt or Math::BigInt::Lite");
41
42SKIP: {
43    # Hexadecimal literals using the "0X" prefix require v5.14.0.
44    skip "perl v5.14.0 required for hexadecimal integer literals"
45      . " with '0X' prefix", "2" if $] < "5.014";
46
47    $x = eval "0XFF";
48    is($x, "255", "hexadecimal integer literal 0XFF");
49    is(ref($x), $class, "value is a $class");
50}
51
52$x = 0377;
53is($x, "255", "octal integer literal 0377");
54is(ref($x), "Math::BigFloat",
55     "value is a Math::BigInt or Math::BigInt::Lite");
56
57SKIP: {
58    # Octal literals using the "0o" prefix require v5.34.0.
59    skip "perl v5.34.0 required for octal floating point literals"
60      . " with '0o' prefix", "4" if $] < "5.034";
61
62    for my $str (qw/ 0o377 0O377 /) {
63        $x = eval $str;
64        is($x, "255", "octal integer literal $str");
65        is(ref($x), "Math::BigFloat",
66     "value is a Math::BigInt or Math::BigInt::Lite");
67    }
68}
69
70$x = 0b11111111;
71is($x, "255", "binary integer literal 0b11111111");
72is(ref($x), "Math::BigFloat",
73     "value is a Math::BigInt or Math::BigInt::Lite");
74
75SKIP: {
76    # Binary literals using the "0B" prefix require v5.14.0.
77    skip "perl v5.14.0 required for binary integer literals"
78      . " with '0B' prefix", "2" if $] < "5.014";
79
80    $x = eval "0B11111111";
81    is($x, "255", "binary integer literal 0B11111111");
82    is(ref($x), $class, "value is a $class");
83}
84
85# These are handled by "float".
86
87$x = 999999999999999999999999999999999999999999999999999999999999999999999999;
88is($x,
89   "999999999999999999999999999999999999999999999999999999999999999999999999",
90   "decimal integer literal " . ("9" x 72));
91is(ref($x), "Math::BigFloat",
92     "value is a Math::BigInt or Math::BigInt::Lite");
93
94$x = 1e72 - 1;
95is($x,
96   "999999999999999999999999999999999999999999999999999999999999999999999999",
97   "literal 1e72 - 1");
98is(ref($x), "Math::BigFloat",
99     "value is a Math::BigInt or Math::BigInt::Lite");
100
101# These are handled by "float".
102
103SKIP: {
104    # Hexadecimal floating point literals require v5.28.0.
105    skip "perl v5.28.0 required for hexadecimal floating point literals",
106      "6" * "2" + "2" * "2" if $] < "5.028";
107
108    for my $str (qw/ 0x1.3ap+8 0X1.3AP+8
109                     0x1.3ap8  0X1.3AP8
110                     0x13a0p-4 0X13A0P-4 /)
111    {
112        $x = eval $str;
113        is($x, "314", "hexadecimal floating point literal $str");
114        is(ref($x), "Math::BigFloat",
115             "value is a Math::BigInt or Math::BigInt::Lite");
116    }
117
118    for my $str (qw/ 0x0.0p+8 0X0.0P+8 /)
119    {
120        $x = eval $str;
121        is($x, "0", "hexadecimal floating point literal $str");
122        is(ref($x), "Math::BigFloat",
123             "value is a Math::BigInt or Math::BigInt::Lite");
124    }
125}
126
127SKIP: {
128    # Octal floating point literals using the "0o" prefix require v5.34.0.
129    skip "perl v5.34.0 required for octal floating point literals"
130      . " with '0o' prefix", "6" * "2" + "6" * "2" if $] < "5.034";
131
132    for my $str (qw/ 0o1.164p+8 0O1.164P+8
133                     0o1.164p8  0O1.164P8
134                     0o11640p-4 0O11640P-4 /)
135    {
136        $x = eval $str;
137        is($x, "314", "octal floating point literal $str");
138        is(ref($x), "Math::BigFloat",
139             "value is a Math::BigInt or Math::BigInt::Lite");
140    }
141
142    for my $str (qw/ 0o0.0p+8 0O0.0P+8
143                     0o0.0p8  0O0.0P8
144                     0o0.0p-8 0O0.0P-8 /)
145    {
146        $x = eval $str;
147        is($x, "0", "octal floating point literal $str");
148        is(ref($x), "Math::BigFloat",
149             "value is a Math::BigInt or Math::BigInt::Lite");
150    }
151}
152
153SKIP: {
154    # Octal floating point literals using the "0" prefix require v5.32.0.
155    skip "perl v5.32.0 required for octal floating point literals",
156      "6" * "2" + "6" * "2" if $] < "5.032";
157
158    for my $str (qw/ 01.164p+8 01.164P+8
159                     01.164p8  01.164P8
160                     011640p-4 011640P-4 /)
161    {
162        $x = eval $str;
163        is($x, "314", "octal floating point literal $str");
164        is(ref($x), "Math::BigFloat",
165             "value is a Math::BigInt or Math::BigInt::Lite");
166    }
167
168    for my $str (qw/ 00.0p+8 00.0P+8
169                     00.0p8 00.0P8
170                     00.0p-8 00.0P-8 /)
171    {
172        $x = eval $str;
173        is($x, "0", "octal floating point literal $str");
174        is(ref($x), "Math::BigFloat",
175             "value is a Math::BigInt or Math::BigInt::Lite");
176    }
177}
178
179SKIP: {
180    # Binary floating point literals require v5.32.0.
181    skip "perl v5.32.0 required for binary floating point literals",
182      "6" * "2" + "6" * "2" if $] < "5.032";
183
184    for my $str (qw/ 0b1.0011101p+8   0B1.0011101P+8
185                     0b1.0011101p8    0B1.0011101P8
186                     0b10011101000p-2 0B10011101000P-2 /)
187    {
188        $x = eval $str;
189        is($x, "314", "binary floating point literal $str");
190        is(ref($x), "Math::BigFloat",
191             "value is a Math::BigInt or Math::BigInt::Lite");
192    }
193
194    for my $str (qw/ 0b0p+8 0B0P+8
195                     0b0p8 0B0P8
196                     0b0p-8 0B0P-8
197                   /)
198    {
199        $x = eval $str;
200        is($x, "0", "binary floating point literal $str");
201        is(ref($x), "Math::BigFloat",
202             "value is a Math::BigInt or Math::BigInt::Lite");
203    }
204}
205
206# These are handled by "integer".
207
208$x = 314;
209is($x, "314", "integer literal 314");
210is(ref($x), "Math::BigFloat",
211     "value is a Math::BigInt or Math::BigInt::Lite");
212
213$x = 0;
214is($x, "0", "integer literal 0");
215is(ref($x), "Math::BigFloat",
216     "value is a Math::BigInt or Math::BigInt::Lite");
217
218$x = 2 ** 255;
219is($x,
220   "578960446186580977117854925043439539266"
221   . "34992332820282019728792003956564819968",
222   "2 ** 255");
223is(ref($x), "Math::BigFloat",
224     "value is a Math::BigInt or Math::BigInt::Lite");
225
226# These are handled by "binary".
227
228{
229    no warnings "portable";     # protect against "non-portable" warnings
230
231    # hexadecimal constant
232    $x = 0x123456789012345678901234567890;
233    is($x,
234       "94522879687365475552814062743484560",
235       "hexadecimal constant 0x123456789012345678901234567890");
236    is(ref($x), "Math::BigFloat",
237         "value is a Math::BigInt or Math::BigInt::Lite");
238
239    # octal constant
240    $x = 012345676543210123456765432101234567654321;
241    is($x,
242       "1736132869400711976876385488263403729",
243       "octal constant 012345676543210123456765432101234567654321");
244    is(ref($x), "Math::BigFloat",
245         "value is a Math::BigInt or Math::BigInt::Lite");
246
247    # binary constant
248    $x = 0b01010100011001010110110001110011010010010110000101101101;
249    is($x,
250       "23755414508757357",
251       "binary constant 0b0101010001100101011011000111"
252       . "0011010010010110000101101101");
253    is(ref($x), "Math::BigFloat",
254         "value is a Math::BigInt or Math::BigInt::Lite");
255}
256
257################################################################################
258# The following tests are unique to $class.
259
260# These are handled by "float".
261
262$x = 0.999999999999999999999999999999999999999999999999999999999999999999999999;
263is($x,
264   "0.999999999999999999999999999999999999999999999999999999999999999999999999",
265   "decimal floating point literal 0." . ("9" x 72));
266is(ref($x), $class, "value is a $class");
267
268$x = 1e72 - 0.1;
269is($x,
270   "999999999999999999999999999999999999999999999999999999999999999999999999.9",
271   "literal 1e72 - 0.1");
272is(ref($x), $class, "value is a $class");
273
274# These are handled by "float".
275
276SKIP: {
277    # Hexadecimal floating point literals require v5.28.0.
278    skip "perl v5.22.0 required for hexadecimal floating point literals",
279      "6" * "2" if $] < "5.028";
280
281    for my $str (qw/ 0x1.92p+1 0X1.92P+1
282                     0x1.92p1 0X1.92P1
283                     0x19.2p-3 0X19.2P-3 /)
284    {
285        $x = eval $str;
286        is($x, "3.140625", "hexadecimal floating point literal $str");
287        is(ref($x), $class, "value is a $class");
288    }
289}
290
291SKIP: {
292    # Octal floating point literals using the "0o" prefix require v5.34.0.
293    skip "perl v5.34.0 required for octal floating point literals"
294      . " with '0o' prefix", "6" * "2" if $] < "5.034";
295
296    for my $str (qw/ 0o1.444p+1 0O1.444P+1
297                     0o1.444p1  0O1.444P1
298                     0o14.44p-2 0O14.44P-2 /)
299    {
300        $x = eval $str;
301        is($x, "3.140625", "octal floating point literal $str");
302        is(ref($x), $class, "value is a $class");
303    }
304}
305
306SKIP: {
307    # Octal floating point literals using the "0" prefix require v5.32.0.
308    skip "perl v5.32.0 required for octal floating point literals",
309      "6" * "2" if $] < "5.032";
310
311    for my $str (qw/ 01.444p+1 01.444P+1
312                     01.444p1  01.444P1
313                     014.44p-2 014.44P-2 /)
314    {
315        $x = eval $str;
316        is($x, "3.140625", "octal floating point literal $str");
317        is(ref($x), $class, "value is a $class");
318    }
319}
320
321SKIP: {
322    # Binary floating point literals require v5.32.0.
323    skip "perl v5.32.0 required for binary floating point literals",
324      "6" * "2" if $] < "5.032";
325
326    for my $str (qw/ 0b1.1001001p+1 0B1.1001001P+1
327                     0b1.1001001p1  0B1.1001001P1
328                     0b110.01001p-1 0B110.01001P-1 /)
329    {
330        $x = eval $str;
331        is($x, "3.140625", "binary floating point literal $str");
332        is(ref($x), $class, "value is a $class");
333    }
334}
335
336is(1.0 / 3.0, "0.3333333333333333333333333333333333333333",
337   "1.0 / 3.0 = 0.3333333333333333333333333333333333333333");
338