authorgravatar for 12179851+leesongun@users.noreply.github.comleesongun <12179851+leesongun@users.noreply.github.com> 2022-05-17 07:28:20+09:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-16 18:28:20-04:00
log1de7b8d26c2d283b2e494dd6b4cecfa4d44c441d
treee9339fef275d3a0b9166c561afe41b72be69fa27
parent1392c24166f9edc1dd22c979689dcec456292ef4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

`std.math.powi`: use standard definition of underflow/overflow, implement `u0`, `i0`, `i1` edge case (#11499)


1 files changed, 101 insertions(+), 92 deletions(-)

lib/std/math/powi.zig+101-92
......@@ -10,108 +10,94 @@ const testing = std.testing;
1010
1111/// Returns the power of x raised by the integer y (x^y).
1212///
13/// Special Cases:
14/// - powi(x, +-0) = 1 for any x
15/// - powi(0, y) = 0 for any y
16/// - powi(1, y) = 1 for any y
17/// - powi(-1, y) = -1 for y an odd integer
18/// - powi(-1, y) = 1 for y an even integer
19/// - powi(x, y) = Overflow for y >= @sizeOf(x) - 1 or y > 0
20/// - powi(x, y) = Underflow for y > @sizeOf(x) - 1 or y < 0
13/// Errors:
14/// - Overflow: Integer overflow or Infinity
15/// - Underflow: Absolute value of result smaller than 1
16/// Edge case rules ordered by precedence:
17/// - powi(T, x, 0) = 1 unless T is i1, i0, u0
18/// - powi(T, 0, x) = 0 when x > 0
19/// - powi(T, 0, x) = Overflow
20/// - powi(T, 1, y) = 1
21/// - powi(T, -1, y) = -1 for y an odd integer
22/// - powi(T, -1, y) = 1 unless T is i1, i0, u0
23/// - powi(T, -1, y) = Overflow
24/// - powi(T, x, y) = Overflow when y >= @bitSizeOf(x)
25/// - powi(T, x, y) = Underflow when y < 0
2126pub fn powi(comptime T: type, x: T, y: T) (error{
2227 Overflow,
2328 Underflow,
2429}!T) {
25 const info = @typeInfo(T);
30 const bit_size = @typeInfo(T).Int.bits;
31
32 // `y & 1 == 0` won't compile when `does_one_overflow`.
33 const does_one_overflow = math.maxInt(T) < 1;
34 const is_y_even = !does_one_overflow and y & 1 == 0;
35
36 if (x == 1 or y == 0 or (x == -1 and is_y_even)) {
37 if (does_one_overflow) {
38 return error.Overflow;
39 } else {
40 return 1;
41 }
42 }
2643
27 comptime assert(@typeInfo(T) == .Int);
44 if (x == -1) {
45 return -1;
46 }
2847
29 // powi(x, +-0) = 1 for any x
30 if (y == 0 or y == -0) {
31 return 1;
48 if (x == 0) {
49 if (y > 0) {
50 return 0;
51 } else {
52 // Infinity/NaN, not overflow in strict sense
53 return error.Overflow;
54 }
55 }
56 // x >= 2 or x <= -2 from this point
57 if (y >= bit_size) {
58 return error.Overflow;
59 }
60 if (y < 0) {
61 return error.Underflow;
3262 }
3363
34 switch (x) {
35 // powi(0, y) = 0 for any y
36 0 => return 0,
37
38 // powi(1, y) = 1 for any y
39 1 => return 1,
40
41 else => {
42 // powi(x, y) = Overflow for for y >= @sizeOf(x) - 1 y > 0
43 // powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0
44 const bit_size = @sizeOf(T) * 8;
45 if (info.Int.signedness == .signed) {
46 if (x == -1) {
47 // powi(-1, y) = -1 for for y an odd integer
48 // powi(-1, y) = 1 for for y an even integer
49 if (@mod(y, 2) == 0) {
50 return 1;
51 } else {
52 return -1;
53 }
54 }
55
56 if (x > 0 and y >= bit_size - 1) {
57 return error.Overflow;
58 } else if (x < 0 and y > bit_size - 1) {
59 return error.Underflow;
60 }
61 } else {
62 if (y >= bit_size) {
63 return error.Overflow;
64 }
65 }
64 // invariant :
65 // return value = powi(T, base, exp) * acc;
6666
67 var base = x;
68 var exp = y;
69 var acc: T = 1;
70
71 while (exp > 1) {
72 if (exp & 1 == 1) {
73 if (@mulWithOverflow(T, acc, base, &acc)) {
74 if (x > 0) {
75 return error.Overflow;
76 } else {
77 return error.Underflow;
78 }
79 }
80 }
81
82 exp >>= 1;
83
84 if (@mulWithOverflow(T, base, base, &base)) {
85 if (x > 0) {
86 return error.Overflow;
87 } else {
88 return error.Underflow;
89 }
90 }
91 }
67 var base = x;
68 var exp = y;
69 var acc: T = if (does_one_overflow) unreachable else 1;
9270
93 if (exp == 1) {
94 if (@mulWithOverflow(T, acc, base, &acc)) {
95 if (x > 0) {
96 return error.Overflow;
97 } else {
98 return error.Underflow;
99 }
100 }
71 while (exp > 1) {
72 if (exp & 1 == 1) {
73 if (@mulWithOverflow(T, acc, base, &acc)) {
74 return error.Overflow;
10175 }
76 }
77
78 exp >>= 1;
10279
103 return acc;
104 },
80 if (@mulWithOverflow(T, base, base, &base)) {
81 return error.Overflow;
82 }
10583 }
84
85 if (exp == 1) {
86 if (@mulWithOverflow(T, acc, base, &acc)) {
87 return error.Overflow;
88 }
89 }
90
91 return acc;
10692}
10793
10894test "math.powi" {
109 try testing.expectError(error.Underflow, powi(i8, -66, 6));
110 try testing.expectError(error.Underflow, powi(i16, -13, 13));
111 try testing.expectError(error.Underflow, powi(i32, -32, 21));
112 try testing.expectError(error.Underflow, powi(i64, -24, 61));
113 try testing.expectError(error.Underflow, powi(i17, -15, 15));
114 try testing.expectError(error.Underflow, powi(i42, -6, 40));
95 try testing.expectError(error.Overflow, powi(i8, -66, 6));
96 try testing.expectError(error.Overflow, powi(i16, -13, 13));
97 try testing.expectError(error.Overflow, powi(i32, -32, 21));
98 try testing.expectError(error.Overflow, powi(i64, -24, 61));
99 try testing.expectError(error.Overflow, powi(i17, -15, 15));
100 try testing.expectError(error.Overflow, powi(i42, -6, 40));
115101
116102 try testing.expect((try powi(i8, -5, 3)) == -125);
117103 try testing.expect((try powi(i16, -16, 3)) == -4096);
......@@ -140,15 +126,29 @@ test "math.powi" {
140126 try testing.expectError(error.Overflow, powi(u64, 2342, 63));
141127 try testing.expectError(error.Overflow, powi(u17, 2723, 16));
142128 try testing.expectError(error.Overflow, powi(u42, 8234, 41));
129
130 const minInt = std.math.minInt;
131 try testing.expect((try powi(i8, -2, 7)) == minInt(i8));
132 try testing.expect((try powi(i16, -2, 15)) == minInt(i16));
133 try testing.expect((try powi(i32, -2, 31)) == minInt(i32));
134 try testing.expect((try powi(i64, -2, 63)) == minInt(i64));
135
136 try testing.expectError(error.Underflow, powi(i8, 6, -2));
137 try testing.expectError(error.Underflow, powi(i16, 5, -4));
138 try testing.expectError(error.Underflow, powi(i32, 12, -6));
139 try testing.expectError(error.Underflow, powi(i64, 34, -2));
140 try testing.expectError(error.Underflow, powi(i17, 16, -3));
141 try testing.expectError(error.Underflow, powi(i42, 34, -6));
143142}
144143
145144test "math.powi.special" {
146 try testing.expectError(error.Underflow, powi(i8, -2, 8));
147 try testing.expectError(error.Underflow, powi(i16, -2, 16));
148 try testing.expectError(error.Underflow, powi(i32, -2, 32));
149 try testing.expectError(error.Underflow, powi(i64, -2, 64));
150 try testing.expectError(error.Underflow, powi(i17, -2, 17));
151 try testing.expectError(error.Underflow, powi(i42, -2, 42));
145 try testing.expectError(error.Overflow, powi(i8, -2, 8));
146 try testing.expectError(error.Overflow, powi(i16, -2, 16));
147 try testing.expectError(error.Overflow, powi(i32, -2, 32));
148 try testing.expectError(error.Overflow, powi(i64, -2, 64));
149 try testing.expectError(error.Overflow, powi(i17, -2, 17));
150 try testing.expectError(error.Overflow, powi(i17, -2, 16));
151 try testing.expectError(error.Overflow, powi(i42, -2, 42));
152152
153153 try testing.expect((try powi(i8, -1, 3)) == -1);
154154 try testing.expect((try powi(i16, -1, 2)) == 1);
......@@ -185,3 +185,12 @@ test "math.powi.special" {
185185 try testing.expect((try powi(u17, 16, 0)) == 1);
186186 try testing.expect((try powi(u42, 34, 0)) == 1);
187187}
188
189test "math.powi.narrow" {
190 try testing.expectError(error.Overflow, powi(u0, 0, 0));
191 try testing.expectError(error.Overflow, powi(i0, 0, 0));
192 try testing.expectError(error.Overflow, powi(i1, 0, 0));
193 try testing.expectError(error.Overflow, powi(i1, -1, 0));
194 try testing.expectError(error.Overflow, powi(i1, 0, -1));
195 try testing.expect((try powi(i1, -1, -1)) == -1);
196}