Skip to content

Commit 932ce6d

Browse files
n8shdlang-bot
authored andcommitted
Fix Issue 24140 - Int128.opBinary [+-*/%&|^] with negative long arguments gives wrong answers
1 parent d41877b commit 932ce6d

File tree

1 file changed

+65
-11
lines changed

1 file changed

+65
-11
lines changed

std/int128.d

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public struct Int128
153153
{
154154
return tst(this.data);
155155
}
156+
} // @safe pure nothrow @nogc
156157

157158
/** Support binary arithmetic operators + - * / % & | ^ << >> >>>
158159
* Params:
@@ -190,21 +191,49 @@ public struct Int128
190191
}
191192

192193
/// ditto
193-
Int128 opBinary(string op)(long op2) const
194-
if (op == "+" || op == "-" ||
194+
Int128 opBinary(string op, Int)(const Int op2) const
195+
if ((op == "+" || op == "-" ||
195196
op == "*" || op == "/" || op == "%" ||
196-
op == "&" || op == "|" || op == "^")
197+
op == "&" || op == "|" || op == "^") &&
198+
is(Int : long) && __traits(isIntegral, Int))
197199
{
198-
return mixin("this " ~ op ~ " Int128(0, op2)");
200+
static if (__traits(isUnsigned, Int))
201+
return mixin("this " ~ op ~ " Int128(0, op2)");
202+
else
203+
return mixin("this " ~ op ~ " Int128((cast(long) op2) >> 63 , op2)");
199204
}
200205

201206
/// ditto
202-
Int128 opBinaryRight(string op)(long op2) const
203-
if (op == "+" || op == "-" ||
207+
Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const
208+
if ((op == "+" || op == "-" ||
204209
op == "*" || op == "/" || op == "%" ||
205-
op == "&" || op == "|" || op == "^")
210+
op == "&" || op == "|" || op == "^") &&
211+
is(IntLike : long) && !__traits(isIntegral, IntLike))
206212
{
207-
mixin("return Int128(0, op2) " ~ op ~ " this;");
213+
return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0]));
214+
}
215+
216+
/// ditto
217+
Int128 opBinaryRight(string op, Int)(const Int op2) const
218+
if ((op == "+" || op == "-" ||
219+
op == "*" || op == "/" || op == "%" ||
220+
op == "&" || op == "|" || op == "^") &&
221+
is(Int : long) && __traits(isIntegral, Int))
222+
{
223+
static if (__traits(isUnsigned, Int))
224+
mixin("return Int128(0, op2) " ~ op ~ " this;");
225+
else
226+
mixin("return Int128((cast(long) op2) >> 63, op2) " ~ op ~ " this;");
227+
}
228+
229+
/// ditto
230+
Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const
231+
if ((op == "+" || op == "-" ||
232+
op == "*" || op == "/" || op == "%" ||
233+
op == "&" || op == "|" || op == "^") &&
234+
is(IntLike : long) && !__traits(isIntegral, IntLike))
235+
{
236+
return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0]));
208237
}
209238

210239
/// ditto
@@ -244,16 +273,16 @@ public struct Int128
244273
}
245274

246275
/// ditto
247-
ref Int128 opOpAssign(string op)(long op2)
248-
if (op == "+" || op == "-" ||
276+
ref Int128 opOpAssign(string op, Int)(auto ref Int op2)
277+
if ((op == "+" || op == "-" ||
249278
op == "*" || op == "/" || op == "%" ||
250279
op == "&" || op == "|" || op == "^" ||
251280
op == "<<" || op == ">>" || op == ">>>")
281+
&& is(Int : long))
252282
{
253283
mixin("this = this " ~ op ~ " op2;");
254284
return this;
255285
}
256-
} // @safe pure nothrow @nogc
257286

258287
/** support arithmentic comparison operators < <= > >=
259288
* Params: op2 = right hand operand
@@ -411,4 +440,29 @@ unittest
411440
auto w = Wrapped!ulong(ulong.max);
412441
w.count++; // avoid invalid D-Scanner message that w could have been declared const
413442
assert(c < w);
443+
444+
const zero = Int128(0L);
445+
const one = Int128(1L);
446+
const neg_one = Int128(-1L);
447+
const neg_two = Int128(-2L);
448+
// Correct result with ulong.max:
449+
assert(zero + ulong.max == ulong.max);
450+
assert(one * ulong.max == ulong.max);
451+
assert((neg_one & ulong.max) == ulong.max);
452+
assert((zero | ulong.max) == ulong.max);
453+
assert((zero ^ ulong.max) == ulong.max);
454+
// Correct result with negative arguments:
455+
assert(zero + -1L == -1L);
456+
assert(neg_two * -3L == 6L);
457+
assert(neg_two / -2L == 1L);
458+
assert(neg_two % -2L == 0L);
459+
assert((neg_one & -1L) == -1L);
460+
assert((zero | -1L) == -1L);
461+
assert((zero ^ -1L) == -1L);
462+
// Ensure alias this still works.
463+
{
464+
Int128 a = zero;
465+
assert((a ^= w) == ulong.max);
466+
}
467+
assert((Wrapped!long(-1L) + zero) == -1L);
414468
}

0 commit comments

Comments
 (0)