// 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: true if value is not zero */ bool opCast(T : bool)() const { return tst(this.data); } } // @safe pure nothrow @nogc /** Support casting to an integral type * Params: T = integral type * Returns: low bits of value reinterpreted as T */ T opCast(T : long)() const if (is(byte : T)) { return cast(T) this.data.lo; } /// @safe unittest { const Int128 a = Int128(0xffff_ffff_ffff_ffffL, 0x0123_4567_89ab_cdefL); assert(cast(long) a == 0x0123_4567_89ab_cdefL); assert(cast(int) a == 0x89ab_cdef); assert(cast(byte) a == cast(byte) 0xef); } /** Support casting to floating point type * Params: T = floating point type * Returns: value cast to floating point with environment-dependent * rounding if the value is not exactly representable */ T opCast(T : real)() const { import core.math : ldexp; if (cast(long) this.data.hi >= 0) return ldexp(cast(T) this.data.hi, 64) + this.data.lo; else { const absData = neg(this.data); return -ldexp(cast(T) absData.hi, 64) - absData.lo; } } /// @safe unittest { const Int128 a = Int128(-1L << 60); assert(cast(double) a == -(2.0 ^^ 60)); assert(cast(double) (a * a) == 2.0 ^^ 120); } /** 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, Int)(const Int op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) return mixin("this " ~ op ~ " Int128(0, op2)"); else return mixin("this " ~ op ~ " Int128((cast(long) op2) >> 63 , op2)"); } /// ditto Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// ditto Int128 opBinaryRight(string op, Int)(const Int op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) mixin("return Int128(0, op2) " ~ op ~ " this;"); else mixin("return Int128((cast(long) op2) >> 63, op2) " ~ op ~ " this;"); } /// ditto Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// 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, Int)(auto ref Int op2) if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^" || op == "<<" || op == ">>" || op == ">>>") && is(Int : long)) { mixin("this = this " ~ op ~ " op2;"); return this; } /** support 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 @nogc nothrow pure @safe { return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1; } /// ditto int opCmp(Int)(const Int op2) const @nogc nothrow pure @safe if (is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) return opCmp(Int128(0, op2)); else return opCmp(Int128((cast(long) op2) >> 63, op2)); } /// ditto int opCmp(IntLike)(auto ref IntLike op2) const if (is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opCmp(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /** * Formats `Int128` with either `%d`, `%x`, `%X`, or `%s` (same as `%d`). * * Params: * sink = $(REF_ALTTEXT Output range, isOutputRange, std, range, primitives) * to write to. * fmt = A $(REF FormatSpec, std,format) which controls how the number * is displayed. * * Throws: * $(REF FormatException, std,format) if the format specifier is * not one of 'd', 'x', 'X', 's'. * * See_Also: $(REF formatValue, std,format) */ void toString(Writer, FormatSpec)(scope ref Writer sink, scope const ref FormatSpec fmt) const { import std.range.primitives : put; import std.format : FormatException, Fmt = FormatSpec; static if (is(FormatSpec == Fmt!Char, Char)) { // Puts "Char" into scope if the pattern matches. } static assert(is(Char), "Expecting `FormatSpec` to be instantiation of `std.format.FormatSpec`"); Char[39] buf = void; size_t bufStart = void; Char signChar = 0; if (fmt.spec == 'd' || fmt.spec == 's') { const bool isNeg = 0 > cast(long) this.data.hi; Cent val = isNeg ? neg(this.data) : this.data; immutable Cent radix = { lo: 10, hi: 0 }; Cent modulus; bufStart = buf.length; do { uint x = void; if (ugt(radix, val)) { x = cast(uint) val.lo; val = Cent(0, 0); } else { val = udivmod(val, radix, modulus); x = cast(uint) modulus.lo; } buf[--bufStart] = cast(Char) ('0' + x); } while (tst(val)); if (isNeg) signChar = '-'; else if (fmt.flPlus) signChar = '+'; else if (fmt.flSpace) signChar = ' '; } else if (fmt.spec == 'x' || fmt.spec == 'X') { immutable hexDigits = fmt.spec == 'X' ? "0123456789ABCDEF" : "0123456789abcdef"; ulong a = data.lo; bufStart = buf.length - 1; size_t penPos = buf.length - 1; do { if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0') bufStart = penPos; a >>>= 4; } while (--penPos >= buf.length - 16); a = data.hi; do { if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0') bufStart = penPos; a >>>= 4; } while (--penPos >= buf.length - 32); } else { throw new FormatException("Format specifier not understood: %" ~ fmt.spec); } const minw = (buf.length - bufStart) + int(signChar != 0); const maxw = minw < fmt.width ? fmt.width : minw; const difw = maxw - minw; static void putRepeatedChars(Char c)(scope ref Writer sink, size_t n) { static immutable Char[8] array = [c, c, c, c, c, c, c, c]; foreach (_; 0 .. n / 8) put(sink, array[0 .. 8]); if (n & 7) put(sink, array[0 .. n & 7]); } if (!fmt.flDash && !fmt.flZero && difw) putRepeatedChars!' '(sink, difw); if (signChar) { Char[1] signCharBuf = signChar; put(sink, signCharBuf[0 .. 1]); } if (!fmt.flDash && fmt.flZero && difw) putRepeatedChars!'0'(sink, difw); put(sink, buf[bufStart .. $]); if (fmt.flDash && difw) putRepeatedChars!' '(sink, difw); } /** `toString` is rarely directly invoked; the usual way of using it is via $(REF format, std, format): */ @safe unittest { import std.format : format; assert(format("%s", Int128.max) == "170141183460469231731687303715884105727"); assert(format("%s", Int128.min) == "-170141183460469231731687303715884105728"); assert(format("%x", Int128.max) == "7fffffffffffffffffffffffffffffff"); assert(format("%X", Int128.max) == "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); assert(format("%032X", Int128(123L)) == "0000000000000000000000000000007B"); assert(format("%+ 40d", Int128(123L)) == " +123"); assert(format("%+-40d", Int128(123L)) == "+123 "); } /// Also can format as `wchar` or `dchar`. @safe unittest { import std.conv : to; assert(to!wstring(Int128.max) == "170141183460469231731687303715884105727"w); assert(to!dstring(Int128.max) == "170141183460469231731687303715884105727"d); } enum min = Int128(long.min, 0); /// minimum value enum max = Int128(long.max, ulong.max); /// maximum value}Lines 1–500 of 653. Load more
// 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: true if value is not zero */ bool opCast(T : bool)() const { return tst(this.data); } } // @safe pure nothrow @nogc /** Support casting to an integral type * Params: T = integral type * Returns: low bits of value reinterpreted as T */ T opCast(T : long)() const if (is(byte : T)) { return cast(T) this.data.lo; } /// @safe unittest { const Int128 a = Int128(0xffff_ffff_ffff_ffffL, 0x0123_4567_89ab_cdefL); assert(cast(long) a == 0x0123_4567_89ab_cdefL); assert(cast(int) a == 0x89ab_cdef); assert(cast(byte) a == cast(byte) 0xef); } /** Support casting to floating point type * Params: T = floating point type * Returns: value cast to floating point with environment-dependent * rounding if the value is not exactly representable */ T opCast(T : real)() const { import core.math : ldexp; if (cast(long) this.data.hi >= 0) return ldexp(cast(T) this.data.hi, 64) + this.data.lo; else { const absData = neg(this.data); return -ldexp(cast(T) absData.hi, 64) - absData.lo; } } /// @safe unittest { const Int128 a = Int128(-1L << 60); assert(cast(double) a == -(2.0 ^^ 60)); assert(cast(double) (a * a) == 2.0 ^^ 120); } /** 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, Int)(const Int op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) return mixin("this " ~ op ~ " Int128(0, op2)"); else return mixin("this " ~ op ~ " Int128((cast(long) op2) >> 63 , op2)"); } /// ditto Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// ditto Int128 opBinaryRight(string op, Int)(const Int op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) mixin("return Int128(0, op2) " ~ op ~ " this;"); else mixin("return Int128((cast(long) op2) >> 63, op2) " ~ op ~ " this;"); } /// ditto Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^") && is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// 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, Int)(auto ref Int op2) if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "&" || op == "|" || op == "^" || op == "<<" || op == ">>" || op == ">>>") && is(Int : long)) { mixin("this = this " ~ op ~ " op2;"); return this; } /** support 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 @nogc nothrow pure @safe { return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1; } /// ditto int opCmp(Int)(const Int op2) const @nogc nothrow pure @safe if (is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) return opCmp(Int128(0, op2)); else return opCmp(Int128((cast(long) op2) >> 63, op2)); } /// ditto int opCmp(IntLike)(auto ref IntLike op2) const if (is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opCmp(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /** * Formats `Int128` with either `%d`, `%x`, `%X`, or `%s` (same as `%d`). * * Params: * sink = $(REF_ALTTEXT Output range, isOutputRange, std, range, primitives) * to write to. * fmt = A $(REF FormatSpec, std,format) which controls how the number * is displayed. * * Throws: * $(REF FormatException, std,format) if the format specifier is * not one of 'd', 'x', 'X', 's'. * * See_Also: $(REF formatValue, std,format) */ void toString(Writer, FormatSpec)(scope ref Writer sink, scope const ref FormatSpec fmt) const { import std.range.primitives : put; import std.format : FormatException, Fmt = FormatSpec; static if (is(FormatSpec == Fmt!Char, Char)) { // Puts "Char" into scope if the pattern matches. } static assert(is(Char), "Expecting `FormatSpec` to be instantiation of `std.format.FormatSpec`"); Char[39] buf = void; size_t bufStart = void; Char signChar = 0; if (fmt.spec == 'd' || fmt.spec == 's') { const bool isNeg = 0 > cast(long) this.data.hi; Cent val = isNeg ? neg(this.data) : this.data; immutable Cent radix = { lo: 10, hi: 0 }; Cent modulus; bufStart = buf.length; do { uint x = void; if (ugt(radix, val)) { x = cast(uint) val.lo; val = Cent(0, 0); } else { val = udivmod(val, radix, modulus); x = cast(uint) modulus.lo; } buf[--bufStart] = cast(Char) ('0' + x); } while (tst(val)); if (isNeg) signChar = '-'; else if (fmt.flPlus) signChar = '+'; else if (fmt.flSpace) signChar = ' '; } else if (fmt.spec == 'x' || fmt.spec == 'X') { immutable hexDigits = fmt.spec == 'X' ? "0123456789ABCDEF" : "0123456789abcdef"; ulong a = data.lo; bufStart = buf.length - 1; size_t penPos = buf.length - 1; do { if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0') bufStart = penPos; a >>>= 4; } while (--penPos >= buf.length - 16); a = data.hi; do { if ((buf[penPos] = hexDigits[0xF & cast(uint) a]) != '0') bufStart = penPos; a >>>= 4; } while (--penPos >= buf.length - 32); } else { throw new FormatException("Format specifier not understood: %" ~ fmt.spec); } const minw = (buf.length - bufStart) + int(signChar != 0); const maxw = minw < fmt.width ? fmt.width : minw; const difw = maxw - minw; static void putRepeatedChars(Char c)(scope ref Writer sink, size_t n) { static immutable Char[8] array = [c, c, c, c, c, c, c, c]; foreach (_; 0 .. n / 8) put(sink, array[0 .. 8]); if (n & 7) put(sink, array[0 .. n & 7]); } if (!fmt.flDash && !fmt.flZero && difw) putRepeatedChars!' '(sink, difw); if (signChar) { Char[1] signCharBuf = signChar; put(sink, signCharBuf[0 .. 1]); } if (!fmt.flDash && fmt.flZero && difw) putRepeatedChars!'0'(sink, difw); put(sink, buf[bufStart .. $]); if (fmt.flDash && difw) putRepeatedChars!' '(sink, difw); } /** `toString` is rarely directly invoked; the usual way of using it is via $(REF format, std, format): */ @safe unittest { import std.format : format; assert(format("%s", Int128.max) == "170141183460469231731687303715884105727"); assert(format("%s", Int128.min) == "-170141183460469231731687303715884105728"); assert(format("%x", Int128.max) == "7fffffffffffffffffffffffffffffff"); assert(format("%X", Int128.max) == "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); assert(format("%032X", Int128(123L)) == "0000000000000000000000000000007B"); assert(format("%+ 40d", Int128(123L)) == " +123"); assert(format("%+-40d", Int128(123L)) == "+123 "); } /// Also can format as `wchar` or `dchar`. @safe unittest { import std.conv : to; assert(to!wstring(Int128.max) == "170141183460469231731687303715884105727"w); assert(to!dstring(Int128.max) == "170141183460469231731687303715884105727"d); } enum min = Int128(long.min, 0); /// minimum value enum max = Int128(long.max, ulong.max); /// maximum value }