@@ -1156,7 +1156,7 @@ public static Int128 Log2(Int128 value)
1156
1156
}
1157
1157
1158
1158
// We simplify the logic here by just doing unsigned division on the
1159
- // one 's complement representation and then taking the correct sign.
1159
+ // two 's complement representation and then taking the correct sign.
1160
1160
1161
1161
ulong sign = ( left . _upper ^ right . _upper ) & ( 1UL << 63 ) ;
1162
1162
@@ -1172,17 +1172,13 @@ public static Int128 Log2(Int128 value)
1172
1172
1173
1173
UInt128 result = ( UInt128 ) ( left ) / ( UInt128 ) ( right ) ;
1174
1174
1175
- if ( result == 0U )
1176
- {
1177
- sign = 0 ;
1178
- }
1179
- else if ( sign != 0 )
1175
+ if ( sign != 0 )
1180
1176
{
1181
1177
result = ~ result + 1U ;
1182
1178
}
1183
1179
1184
1180
return new Int128 (
1185
- result . Upper | sign ,
1181
+ result . Upper ,
1186
1182
result . Lower
1187
1183
) ;
1188
1184
}
@@ -1227,36 +1223,8 @@ public static Int128 Log2(Int128 value)
1227
1223
/// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" />
1228
1224
public static Int128 operator % ( Int128 left , Int128 right )
1229
1225
{
1230
- // We simplify the logic here by just doing unsigned modulus on the
1231
- // one's complement representation and then taking the correct sign.
1232
-
1233
- ulong sign = ( left . _upper ^ right . _upper ) & ( 1UL << 63 ) ;
1234
-
1235
- if ( IsNegative ( left ) )
1236
- {
1237
- left = ~ left + 1U ;
1238
- }
1239
-
1240
- if ( IsNegative ( right ) )
1241
- {
1242
- right = ~ right + 1U ;
1243
- }
1244
-
1245
- UInt128 result = ( UInt128 ) ( left ) % ( UInt128 ) ( right ) ;
1246
-
1247
- if ( result == 0U )
1248
- {
1249
- sign = 0 ;
1250
- }
1251
- else if ( sign != 0 )
1252
- {
1253
- result = ~ result + 1U ;
1254
- }
1255
-
1256
- return new Int128 (
1257
- result . Upper | sign ,
1258
- result . Lower
1259
- ) ;
1226
+ Int128 quotient = left / right ;
1227
+ return left - ( quotient * right ) ;
1260
1228
}
1261
1229
1262
1230
//
@@ -1273,76 +1241,43 @@ public static Int128 Log2(Int128 value)
1273
1241
/// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" />
1274
1242
public static Int128 operator * ( Int128 left , Int128 right )
1275
1243
{
1276
- // We simplify the logic here by just doing unsigned multiplication on
1277
- // the one's complement representation and then taking the correct sign.
1278
-
1279
- ulong sign = ( left . _upper ^ right . _upper ) & ( 1UL << 63 ) ;
1280
-
1281
- if ( IsNegative ( left ) )
1282
- {
1283
- left = ~ left + 1U ;
1284
- }
1285
-
1286
- if ( IsNegative ( right ) )
1287
- {
1288
- right = ~ right + 1U ;
1289
- }
1290
-
1291
- UInt128 result = ( UInt128 ) ( left ) * ( UInt128 ) ( right ) ;
1292
-
1293
- if ( result == 0U )
1294
- {
1295
- sign = 0 ;
1296
- }
1297
- else if ( sign != 0 )
1298
- {
1299
- result = ~ result + 1U ;
1300
- }
1301
-
1302
- return new Int128 (
1303
- result . Upper | sign ,
1304
- result . Lower
1305
- ) ;
1244
+ // Multiplication is the same for signed and unsigned provided the "upper" bits aren't needed
1245
+ return ( Int128 ) ( ( UInt128 ) ( left ) * ( UInt128 ) ( right ) ) ;
1306
1246
}
1307
1247
1308
1248
/// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_CheckedMultiply(TSelf, TOther)" />
1309
1249
public static Int128 operator checked * ( Int128 left , Int128 right )
1310
1250
{
1311
- // We simplify the logic here by just doing unsigned multiplication on
1312
- // the one's complement representation and then taking the correct sign.
1313
-
1314
- ulong sign = ( left . _upper ^ right . _upper ) & ( 1UL << 63 ) ;
1315
-
1316
- if ( IsNegative ( left ) )
1317
- {
1318
- left = ~ left + 1U ;
1319
- }
1251
+ Int128 upper = BigMul ( left , right , out Int128 lower ) ;
1320
1252
1321
- if ( IsNegative ( right ) )
1253
+ if ( ( ( upper != 0 ) || ( lower < 0 ) ) && ( ( ~ upper != 0 ) || ( lower >= 0 ) ) )
1322
1254
{
1323
- right = ~ right + 1U ;
1324
- }
1255
+ // The upper bits can safely be either Zero or AllBitsSet
1256
+ // where the former represents a positive value and the
1257
+ // latter a negative value.
1258
+ //
1259
+ // However, when the upper bits are Zero, we also need to
1260
+ // confirm the lower bits are positive, otherwise we have
1261
+ // a positive value greater than MaxValue and should throw
1262
+ //
1263
+ // Likewise, when the upper bits are AllBitsSet, we also
1264
+ // need to confirm the lower bits are negative, otherwise
1265
+ // we have a large negative value less than MinValue and
1266
+ // should throw.
1325
1267
1326
- UInt128 result = checked ( ( UInt128 ) ( left ) * ( UInt128 ) ( right ) ) ;
1327
-
1328
- if ( ( long ) ( result . Upper ) < 0 )
1329
- {
1330
1268
ThrowHelper . ThrowOverflowException ( ) ;
1331
1269
}
1332
1270
1333
- if ( result == 0U )
1334
- {
1335
- sign = 0 ;
1336
- }
1337
- else if ( sign != 0 )
1338
- {
1339
- result = ~ result + 1U ;
1340
- }
1271
+ return lower ;
1272
+ }
1341
1273
1342
- return new Int128 (
1343
- result . Upper | sign ,
1344
- result . Lower
1345
- ) ;
1274
+ internal static Int128 BigMul ( Int128 left , Int128 right , out Int128 lower )
1275
+ {
1276
+ // This follows the same logic as is used in `long Math.BigMul(long, long, out long)`
1277
+
1278
+ UInt128 upper = UInt128 . BigMul ( ( UInt128 ) ( left ) , ( UInt128 ) ( right ) , out UInt128 ulower ) ;
1279
+ lower = ( Int128 ) ( ulower ) ;
1280
+ return ( Int128 ) ( upper ) - ( ( left >> 127 ) & right ) - ( ( right >> 127 ) & left ) ;
1346
1281
}
1347
1282
1348
1283
//
0 commit comments