| /* |
| ****************************************************************************** |
| * Copyright (C) 1997-2001, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ****************************************************************************** |
| * file name: llong.cpp |
| * encoding: US-ASCII |
| * tab size: 8 (not used) |
| * indentation:4 |
| * |
| * Modification history |
| * Date Name Comments |
| * 10/11/2001 Doug Ported from ICU4J (thanks to Mike Cowlishaw) |
| */ |
| |
| #include "llong.h" |
| #include <float.h> |
| |
| U_NAMESPACE_BEGIN |
| |
| #if 0 |
| /* |
| * This should work, I think, but SOLARISCC -xO3 can't handle it. |
| * Works with SOLARISGCC, SOLARISCC -g, Win32... |
| * |
| */ |
| const llong& llong::kMaxValue = llong(0x7fffffff, 0xffffffff); |
| const llong& llong::kMinValue = llong(0x80000000, 0x0); |
| const llong& llong::kMinusOne = llong(0xffffffff, 0xffffffff); |
| const llong& llong::kZero = llong(0x0, 0x0); |
| const llong& llong::kOne = llong(0x0, 0x1); |
| const llong& llong::kTwo = llong(0x0, 0x2); |
| const llong& llong::kMaxDouble = llong(0x200000, 0x0); |
| const llong& llong::kMinDouble = -kMaxDouble; |
| |
| static llong kMaxValueObj(0x7fffffff, 0xffffffff); |
| static llong kMinValueObj(0x80000000, 0x0); |
| static llong kMinusOneObj(0xffffffff, 0xffffffff); |
| static llong kZeroObj(0x0, 0x0); |
| static llong kOneObj(0x0, 0x1); |
| static llong kTwoObj(0x0, 0x2); |
| static llong kMaxDoubleObj(0x200000, 0x0); |
| static llong kMinDoubleObj(-kMaxDoubleObj); |
| |
| const llong& llong::kMaxValue = kMaxValueObj; |
| const llong& llong::kMinValue = kMinValueObj; |
| const llong& llong::kMinusOne = kMinusOneObj; |
| const llong& llong::kZero = kZeroObj; |
| const llong& llong::kOne = kOneObj; |
| const llong& llong::kTwo = kTwoObj; |
| const llong& llong::kMaxDouble = kMaxDoubleObj; |
| const llong& llong::kMinDouble = kMinDoubleObj; |
| |
| const double llong::kDMax = llong_asDouble(kMaxDouble); |
| const double llong::kDMin = -kDMax; |
| #endif |
| |
| #define SQRT231 46340 |
| |
| // Keep all math as a double. Solaris 64-bit fails otherwise |
| const double llong::kD32 = ((double)((uint32_t)0xffffffffu)) + 1.0; |
| |
| llong::llong(double d) { // avoid dependency on bit representation of double |
| if (uprv_isNaN(d)) { |
| hi = 0; |
| lo = 0; /* zero */ |
| } else { |
| double mant = uprv_maxMantissa(); |
| if (d < -mant) { |
| d = -mant; |
| } else if (d > mant) { |
| d = mant; |
| } |
| UBool neg = d < 0; |
| if (neg) { |
| d = -d; |
| } |
| d = uprv_floor(d); |
| hi = (int32_t)uprv_floor(d / kD32); |
| d -= kD32 * hi; |
| lo = (uint32_t)d; |
| if (neg) { |
| negate(); |
| } |
| } |
| } |
| |
| llong& llong::operator*=(const llong& rhs) |
| { |
| // optimize small positive multiplications |
| if (hi == 0 && rhs.hi == 0 && lo < SQRT231 && rhs.lo < SQRT231) { |
| lo *= rhs.lo; |
| } else { |
| int retry = 0; |
| |
| llong a(*this); |
| if (a.isNegative()) { |
| retry = 1; |
| a.negate(); |
| } |
| |
| llong b(rhs); |
| if (b.isNegative()) { |
| retry = 1; |
| b.negate(); |
| } |
| |
| llong r; |
| // optimize small negative multiplications |
| if (retry && a.hi == 0 && b.hi == 0 && a.lo < SQRT231 && b.lo < SQRT231) { |
| r.lo = a.lo * b.lo; |
| } else { |
| if (a < b) { |
| llong t = a; |
| a = b; |
| b = t; |
| } |
| while (b.notZero()) { |
| if (b.lo & 0x1) { |
| r += a; |
| } |
| b.ushr(1); // in case someone came in with 0x80000000, 0x80000000 for b |
| a <<= 1; |
| } |
| } |
| if (isNegative() != rhs.isNegative()) { |
| r.negate(); |
| } |
| *this = r; |
| } |
| return *this; |
| } |
| |
| llong& llong::operator/=(const llong& rhs) |
| { |
| if (isZero()) { |
| return *this; |
| } |
| int32_t sign = 1; |
| llong a(*this); |
| if (a.isNegative()) { |
| sign = -1; |
| a.negate(); |
| } |
| llong b(rhs); |
| if (b.isNegative()) { |
| sign = -sign; |
| b.negate(); |
| } |
| |
| if (b.isZero()) { // should throw div by zero error |
| *this = sign < 0 ? llong(0x80000000, 0) : llong(0x7fffffff, 0xffffffff); |
| } else if (a.hi == 0 && b.hi == 0) { |
| *this = (int32_t)(sign * (a.lo / b.lo)); |
| } else if (b > a) { |
| hi = 0; |
| lo = 0; /* zero */ |
| } else if (b == a) { |
| *this = sign; |
| } else { |
| llong r; |
| llong m((int32_t)1); |
| |
| while (b.ule(a)) { // a positive so topmost bit is 0, this will always terminate |
| m <<= 1; |
| b <<= 1; |
| } |
| |
| do { |
| m.ushr(1); // don't sign-extend! |
| if (m.isZero()) break; |
| |
| b.ushr(1); |
| if (b <= a) { |
| r |= m; |
| a -= b; |
| } |
| } while (a >= rhs); |
| |
| if (sign < 0) { |
| r.negate(); |
| } |
| *this = r; |
| } |
| return *this; |
| } |
| |
| static const uint8_t asciiDigits[] = { |
| 0x30u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u, |
| 0x38u, 0x39u, 0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u, |
| 0x67u, 0x68u, 0x69u, 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, |
| 0x6fu, 0x70u, 0x71u, 0x72u, 0x73u, 0x74u, 0x75u, 0x76u, |
| 0x77u, 0x78u, 0x79u, 0x7au, |
| }; |
| |
| static const UChar kUMinus = (UChar)0x002d; |
| |
| static const char kMinus = '-'; |
| |
| static const uint8_t digitInfo[] = { |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0x80u, 0x81u, 0x82u, 0x83u, 0x84u, 0x85u, 0x86u, 0x87u, |
| 0x88u, 0x89u, 0, 0, 0, 0, 0, 0, |
| 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u, |
| 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u, |
| 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u, |
| 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0, |
| 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u, |
| 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u, |
| 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u, |
| 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0, |
| }; |
| |
| #ifdef RBNF_DEBUG |
| llong llong::atoll(const char* str, uint32_t radix) |
| { |
| if (radix > 36) { |
| radix = 36; |
| } else if (radix < 2) { |
| radix = 2; |
| } |
| llong lradix(radix); |
| |
| int neg = 0; |
| if (*str == kMinus) { |
| ++str; |
| neg = 1; |
| } |
| llong result; |
| uint8_t b; |
| while ((b = digitInfo[*str++]) && ((b &= 0x7f) < radix)) { |
| result *= lradix; |
| result += (int32_t)b; |
| } |
| if (neg) { |
| result.negate(); |
| } |
| return result; |
| } |
| #endif |
| |
| llong llong::utoll(const UChar* str, uint32_t radix) |
| { |
| if (radix > 36) { |
| radix = 36; |
| } else if (radix < 2) { |
| radix = 2; |
| } |
| llong lradix(radix); |
| |
| int neg = 0; |
| if (*str == kUMinus) { |
| ++str; |
| neg = 1; |
| } |
| llong result; |
| UChar c; |
| uint8_t b; |
| while (((c = *str++) < 0x0080) && (b = digitInfo[c]) && ((b &= 0x7f) < radix)) { |
| result *= lradix; |
| result += (int32_t)b; |
| } |
| if (neg) { |
| result.negate(); |
| } |
| return result; |
| } |
| |
| #ifdef RBNF_DEBUG |
| uint32_t llong::lltoa(char* buf, uint32_t len, uint32_t radix, UBool raw) const |
| { |
| if (radix > 36) { |
| radix = 36; |
| } else if (radix < 2) { |
| radix = 2; |
| } |
| llong base(radix); |
| |
| char* p = buf; |
| llong w(*this); |
| if (len && w.isNegative() && (radix == 10) && !raw) { |
| w.negate(); |
| *p++ = kMinus; |
| --len; |
| } else if (len && w.isZero()) { |
| *p++ = (char)raw ? 0 : asciiDigits[0]; |
| --len; |
| } |
| |
| while (len && w.notZero()) { |
| llong n = w / base; |
| llong m = n * base; |
| int32_t d = (w-m).asInt(); |
| *p++ = raw ? (char)d : asciiDigits[d]; |
| w = n; |
| --len; |
| } |
| if (len) { |
| *p = 0; // null terminate if room for caller convenience |
| } |
| |
| len = p - buf; |
| if (*buf == kMinus) { |
| ++buf; |
| } |
| while (--p > buf) { |
| char c = *p; |
| *p = *buf; |
| *buf = c; |
| ++buf; |
| } |
| |
| return len; |
| } |
| #endif |
| |
| uint32_t llong::lltou(UChar* buf, uint32_t len, uint32_t radix, UBool raw) const |
| { |
| if (radix > 36) { |
| radix = 36; |
| } else if (radix < 2) { |
| radix = 2; |
| } |
| llong base(radix); |
| |
| UChar* p = buf; |
| llong w(*this); |
| if (len && w.isNegative() && (radix == 10) && !raw) { |
| w.negate(); |
| *p++ = kUMinus; |
| --len; |
| } else if (len && w.isZero()) { |
| *p++ = (UChar)raw ? 0 : asciiDigits[0]; |
| --len; |
| } |
| |
| while (len && w.notZero()) { |
| llong n = w / base; |
| llong m = n * base; |
| int32_t d = (w-m).asInt(); |
| *p++ = (UChar)(raw ? d : asciiDigits[d]); |
| w = n; |
| --len; |
| } |
| if (len) { |
| *p = 0; // null terminate if room for caller convenience |
| } |
| |
| len = p - buf; |
| if (*buf == kUMinus) { |
| ++buf; |
| } |
| while (--p > buf) { |
| UChar c = *p; |
| *p = *buf; |
| *buf = c; |
| ++buf; |
| } |
| |
| return len; |
| } |
| |
| U_NAMESPACE_END |