From 60f5bfa45a4e14038d48d8489b9304923d463867 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 2 Apr 2022 15:33:28 -0700 Subject: [PATCH] add std.int128 --- .dscanner.ini | 3 +- posix.mak | 2 +- std/int128.d | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++ win32.mak | 2 + win64.mak | 1 + 5 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 std/int128.d diff --git a/.dscanner.ini b/.dscanner.ini index 4863d3220ba..0b99b5127b7 100644 --- a/.dscanner.ini +++ b/.dscanner.ini @@ -255,6 +255,7 @@ has_public_example="-etc.c.curl,\ -std.internal.math.gammafunction,\ -std.internal.scopebuffer,\ -std.internal.test.dummyrange,\ +-std.int128,\ -std.json,\ -std.mathspecial,\ -std.mmfile,\ @@ -403,7 +404,7 @@ redundant_attributes_check="-std.concurrency,-std.digest.md,-std.digest.ripemd,- style_check="+disabled" ;style_check="-etc.c.curl,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.comparison,-std.algorithm.internal,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.sorting,-std.array,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.compiler,-std.container.array,-std.conv,-std.datetime.date,-std.datetime.interval,-std.datetime.systime,-std.digest,-std.digest.murmurhash,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bucketizer,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.null_allocator,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.common,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.allocator.mmap_allocator,-std.experimental.checkedint,-std.experimental.typecons,-std.format,-std.functional,-std.getopt,-std.internal.digest.sha_SSSE3,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.unicode_tables,-std.json,-std.math,-std.meta,-std.numeric,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.primitives,-std.regex,-std.regex.internal.ir,-std.regex.internal.kickstart,-std.signals,-std.socket,-std.stdio,-std.string,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.zlib" ; Checks for undocumented public declarations -undocumented_declaration_check="-etc.c.curl,-etc.c.odbc.sql,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.sorting,-std.array,-std.ascii,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.c.linux.socket,-std.c.osx.socket,-std.c.process,-std.compiler,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.quantizer,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.typecons,-std.file,-std.format,-std.functional,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.test.uda,-std.internal.windows.advapi32,-std.json,-std.math,-std.mmfile,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.regex,-std.regex.internal.parser,-std.signals,-std.socket,-std.stdio,-std.string,-std.system,-std.traits,-std.uni,-std.utf,-std.variant,-std.windows.charset,-std.windows.registry,-std.windows.syserror,-std.xml,-std.zip,-std.zlib" +undocumented_declaration_check="-etc.c.curl,-etc.c.odbc.sql,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.sorting,-std.array,-std.ascii,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.c.linux.socket,-std.c.osx.socket,-std.c.process,-std.compiler,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.quantizer,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.checkedint,-std.experimental.logger.core,-std.experimental.typecons,-std.file,-std.format,-std.functional,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.test.uda,-std.internal.windows.advapi32,-std.json,-std.math,-std.mmfile,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.regex,-std.regex.internal.parser,-std.signals,-std.socket,-std.stdio,-std.string,-std.system,-std.traits,-std.uni,-std.utf,-std.variant,-std.windows.charset,-std.windows.registry,-std.windows.syserror,-std.xml,-std.zip,-std.zlib,-std.int128" ; Checks for unused labels unused_label_check="-std.conv,-std.internal.math.biguintx86,-std.regex.internal.thompson,-std.signals,-std.uni" ; Checks for unused function parameters diff --git a/posix.mak b/posix.mak index af25eab005d..023e810b236 100644 --- a/posix.mak +++ b/posix.mak @@ -210,7 +210,7 @@ STD_PACKAGES = std $(addprefix std/,\ PACKAGE_std = array ascii base64 bigint bitmanip checkedint compiler complex concurrency \ conv csv demangle encoding exception file \ - functional getopt json mathspecial meta mmfile numeric \ + functional getopt int128 json mathspecial meta mmfile numeric \ outbuffer package parallelism path process random signals socket stdint \ stdio string sumtype system traits typecons \ uri utf uuid variant xml zip zlib diff --git a/std/int128.d b/std/int128.d new file mode 100644 index 00000000000..fc992f82422 --- /dev/null +++ b/std/int128.d @@ -0,0 +1,374 @@ +// Written in the D programming language +/** + * Implements a signed 128 bit integer type. + * + Author: Walter Bright + Copyright: Copyright (c) 2022, D Language Foundation + License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) + Source: $(PHOBOSSRC std/int128.d) + */ +module std.int128; + +private import core.int128; + + +/*********************************** + * 128 bit signed integer type. + */ + +public struct Int128 +{ + @safe pure nothrow @nogc: + + Cent data; /// core.int128.Cent + + /**************** + * Construct an `Int128` from a `long` value. + * The upper 64 bits are formed by sign extension. + * Params: + * lo = signed lower 64 bits + */ + this(long lo) + { + data.lo = lo; + data.hi = lo < 0 ? ~0L : 0; + } + + /**************** + * Construct an `Int128` from a `ulong` value. + * The upper 64 bits are set to zero. + * Params: + * lo = unsigned lower 64 bits + */ + this(ulong lo) + { + data.lo = lo; + data.hi = 0; + } + + /**************** + * Construct an `Int128` from a `long` value. + * Params: + * hi = upper 64 bits + * lo = lower 64 bits + */ + this(long hi, long lo) + { + data.hi = hi; + data.lo = lo; + } + + /******************** + * Construct an `Int128` from a `Cent`. + * Params: + * data = Cent data + */ + this(Cent data) + { + this.data = data; + } + + /******************** + * Returns: hash value for Int128 + */ + size_t toHash() const + { + return cast(size_t)((data.lo & 0xFFFF_FFFF) + (data.hi & 0xFFFF_FFFF) + (data.lo >> 32) + (data.hi >> 32)); + } + + /************************ + * Compare for equality + * Params: lo = signed value to compare with + * Returns: true if Int128 equals value + */ + bool opEquals(long lo) const + { + return data.lo == lo && data.hi == (lo >> 63); + } + + /************************ + * Compare for equality + * Params: lo = unsigned value to compare with + * Returns: true if Int128 equals value + */ + bool opEquals(ulong lo) const + { + return data.hi == 0 && data.lo == lo; + } + + /************************ + * Compare for equality + * Params: op2 = value to compare with + * Returns: true if Int128 equals value + */ + bool opEquals(Int128 op2) const + { + return data.hi == op2.data.hi && data.lo == op2.data.lo; + } + + /** Support unary arithmentic operator + + * Params: op = "+" + * Returns: lvalue of result + */ + Int128 opUnary(string op)() const + if (op == "+") + { + return this; + } + + /** Support unary arithmentic operator - ~ + * Params: op = "-", "~" + * Returns: lvalue of result + */ + Int128 opUnary(string op)() const + if (op == "-" || op == "~") + { + static if (op == "-") + return Int128(neg(this.data)); + else static if (op == "~") + return Int128(com(this.data)); + } + + /** Support unary arithmentic operator ++ -- + * Params: op = "++", "--" + * Returns: lvalue of result + */ + Int128 opUnary(string op)() + if (op == "++" || op == "--") + { + static if (op == "++") + this.data = inc(this.data); + else static if (op == "--") + this.data = dec(this.data); + else + static assert(0, op); + return this; + } + + /** Support casting to a bool + * Params: T = bool + * Returns: boolean result + */ + bool opCast(T : bool)() const + { + return tst(this.data); + } + + /** Support binary arithmetic operators + - * / % & | ^ << >> >>> + * Params: + * op = one of the arithmetic binary operators + * op2 = second operand + * Returns: value after the operation is applied + */ + Int128 opBinary(string op)(Int128 op2) const + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") + { + static if (op == "+") + return Int128(add(this.data, op2.data)); + else static if (op == "-") + return Int128(sub(this.data, op2.data)); + else static if (op == "*") + return Int128(mul(this.data, op2.data)); + else static if (op == "/") + return Int128(div(this.data, op2.data)); + else static if (op == "%") + { + Cent modulus; + divmod(this.data, op2.data, modulus); + return Int128(modulus); + } + else static if (op == "&") + return Int128(and(this.data, op2.data)); + else static if (op == "|") + return Int128(or(this.data, op2.data)); + else static if (op == "^") + return Int128(xor(this.data, op2.data)); + else + static assert(0, "wrong op value"); + } + + /// ditto + Int128 opBinary(string op)(long op2) const + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") + { + return mixin("this " ~ op ~ " Int128(0, op2)"); + } + + /// ditto + Int128 opBinaryRight(string op)(long op2) const + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") + { + mixin("return Int128(0, op2) " ~ op ~ " this;"); + } + + /// ditto + Int128 opBinary(string op)(long op2) const + if (op == "<<") + { + return Int128(shl(this.data, cast(uint) op2)); + } + + /// ditto + Int128 opBinary(string op)(long op2) const + if (op == ">>") + { + return Int128(sar(this.data, cast(uint) op2)); + } + + /// ditto + Int128 opBinary(string op)(long op2) const + if (op == ">>>") + { + return Int128(shr(this.data, cast(uint) op2)); + } + + /** arithmetic assignment operators += -= *= /= %= &= |= ^= <<= >>= >>>= + * Params: op = one of +, -, etc. + * op2 = second operand + * Returns: lvalue of updated left operand + */ + ref Int128 opOpAssign(string op)(Int128 op2) + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^" || + op == "<<" || op == ">>" || op == ">>>") + { + mixin("this = this " ~ op ~ " op2;"); + return this; + } + + /// ditto + ref Int128 opOpAssign(string op)(long op2) + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^" || + op == "<<" || op == ">>" || op == ">>>") + { + mixin("this = this " ~ op ~ " op2;"); + return this; + } + + /** support signed arithmentic comparison operators < <= > >= + * Params: op2 = right hand operand + * Returns: -1 for less than, 0 for equals, 1 for greater than + */ + int opCmp(Int128 op2) const + { + return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1; + } + + /** support signed arithmentic comparison operators < <= > >= + * Params: op2 = right hand operand + * Returns: -1 for less than, 0 for equals, 1 for greater than + */ + int opCmp(long op2) const + { + return opCmp(Int128(0, op2)); + } + + enum min = Int128(long.min, 0); /// minimum value + enum max = Int128(long.max, ulong.max); /// maximum value +} + +/********************************************* Tests ************************************/ + +version (unittest) +{ +import core.stdc.stdio; + +@trusted void print(Int128 c) +{ + printf("%lld, %lld\n", c.data.hi, c.data.lo); +} + +@trusted void printx(Int128 c) +{ + printf("%llx, %llx\n", c.data.hi, c.data.lo); +} +} + +/// Int128 tests +@safe pure nothrow @nogc +unittest +{ + Int128 c = Int128(5, 6); + assert(c == c); + assert(c == +c); + assert(c == - -c); + assert(~c == Int128(~5, ~6)); + ++c; + assert(c == Int128(5, 7)); + assert(--c == Int128(5, 6)); + assert(!!c); + assert(!Int128()); + + assert(c + Int128(10, 20) == Int128(15, 26)); + assert(c - Int128(1, 2) == Int128(4, 4)); + assert(c * Int128(100, 2) == Int128(610, 12)); + assert(c / Int128(3, 2) == Int128(0, 1)); + assert(c % Int128(3, 2) == Int128(2, 4)); + assert((c & Int128(3, 2)) == Int128(1, 2)); + assert((c | Int128(3, 2)) == Int128(7, 6)); + assert((c ^ Int128(3, 2)) == Int128(6, 4)); + + assert(c + 15 == Int128(5, 21)); + assert(c - 15 == Int128(4, -9)); + assert(c * 15 == Int128(75, 90)); + assert(c / 15 == Int128(0, 6148914691236517205)); + assert(c % 15 == Int128(0, 11)); + assert((c & 15) == Int128(0, 6)); + assert((c | 15) == Int128(5, 15)); + assert((c ^ 15) == Int128(5, 9)); + + assert(15 + c == Int128(5, 21)); + assert(15 - c == Int128(-5, 9)); + assert(15 * c == Int128(75, 90)); + assert(15 / c == Int128(0, 0)); + assert(15 % c == Int128(0, 15)); + assert((15 & c) == Int128(0, 6)); + assert((15 | c) == Int128(5, 15)); + assert((15 ^ c) == Int128(5, 9)); + + assert(c << 1 == Int128(10, 12)); + assert(-c >> 1 == Int128(-3, 9223372036854775805)); + assert(-c >>> 1 == Int128(9223372036854775805, 9223372036854775805)); + + assert((c += 1) == Int128(5, 7)); + assert((c -= 1) == Int128(5, 6)); + assert((c += Int128(0, 1)) == Int128(5, 7)); + assert((c -= Int128(0, 1)) == Int128(5, 6)); + assert((c *= 2) == Int128(10, 12)); + assert((c /= 2) == Int128(5, 6)); + assert((c %= 2) == Int128()); + c += Int128(5, 6); + assert((c *= Int128(10, 20)) == Int128(160, 120)); + assert((c /= Int128(10, 20)) == Int128(0, 15)); + c += Int128(72, 0); + assert((c %= Int128(10, 20)) == Int128(1, -125)); + assert((c &= Int128(3, 20)) == Int128(1, 0)); + assert((c |= Int128(8, 2)) == Int128(9, 2)); + assert((c ^= Int128(8, 2)) == Int128(1, 0)); + c |= Int128(10, 5); + assert((c <<= 1) == Int128(11 * 2, 5 * 2)); + assert((c >>>= 1) == Int128(11, 5)); + c = Int128(long.min, long.min); + assert((c >>= 1) == Int128(long.min >> 1, cast(ulong) long.min >> 1)); + + assert(-Int128.min == Int128.min); + assert(Int128.max + 1 == Int128.min); + + c = Int128(5, 6); + assert(c < Int128(6, 5)); + assert(c > 10); + + c = Int128(-1UL); + assert(c == -1UL); + c = Int128(-1L); + assert(c == -1L); +} diff --git a/win32.mak b/win32.mak index 810fe8dd796..e3840e60af4 100644 --- a/win32.mak +++ b/win32.mak @@ -139,6 +139,7 @@ SRC_STD_3a= \ std\concurrency.d SRC_STD_4= \ + std\int128.d \ std\uuid.d SRC_STD_6= \ @@ -457,6 +458,7 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=ctfe -cov=89 $(UDFLAGS) -main -run std\utf.d $(DMD) -conf= -cov=ctfe -cov=93 $(UDFLAGS) -main -run std\csv.d $(DMD) -conf= -cov=ctfe -cov=95 $(UDFLAGS) -main -run std\complex.d + $(DMD) -conf= -cov=ctfe -cov=95 $(UDFLAGS) -main -run std\int128.d $(DMD) -conf= -cov=ctfe -cov=70 $(UDFLAGS) -main -run std\numeric.d $(DMD) -conf= -cov=ctfe -cov=94 $(UDFLAGS) -main -run std\bigint.d $(DMD) -conf= -cov=ctfe -cov=95 $(UDFLAGS) -main -run std\bitmanip.d diff --git a/win64.mak b/win64.mak index 041fb19c21a..694500b5991 100644 --- a/win64.mak +++ b/win64.mak @@ -145,6 +145,7 @@ SRC_STD_3d= \ std\typecons.d SRC_STD_4= \ + std\int128.d \ std\uuid.d SRC_STD_6a=std\variant.d