You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

338 lines
6.9 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsWide.h"
/////////////////////////////////////////////////////////////////////////
inline hsBool OverflowAdd(UInt32* sum, UInt32 a, UInt32 b)
{
*sum = a + b;
return (a | b) > *sum; // true if overflow
}
/*
Return the overflow from adding the three longs into a signed-wide
wide = (high << 32) + (middle << 16) + low
*/
inline hsBool SetWide3(hsWide* target, Int32 high, UInt32 middle, UInt32 low)
{
hsAssert(high >= 0, "high is neg");
target->fLo = low + (middle << 16);
target->fHi = high + (middle >> 16) + (((low >> 16) + (UInt16)middle) >> 16);
return target->fHi < 0; // true if overflow
}
/////////////////////////////////////////////////////////////////////////
hsWide* hsWide::Mul(Int32 src1, Int32 src2)
{
int neg = 0;
if (src1 < 0)
{ src1 = -src1;
neg = ~0;
}
if (src2 < 0)
{ src2 = -src2;
neg = ~neg;
}
UInt32 a = src1 >> 16;
UInt32 b = (UInt16)src1;
UInt32 c = src2 >> 16;
UInt32 d = (UInt16)src2;
(void)SetWide3(this, a * c, a * d + c * b, b * d);
if (neg)
this->Negate();
return this;
}
hsWide* hsWide::Mul(Int32 A)
{
int neg = 0;
UInt32 B = fLo;
Int32 C = fHi;
Int32 tmp;
UInt32 clo,blo,bhi,alo;
if (A < 0)
{ A = -A;
neg = ~0;
}
if (WIDE_ISNEG(C, B))
{ WIDE_NEGATE(C, B);
neg = ~neg;
}
UInt32 ahi = A >> 16;
UInt32 chi = C >> 16;
if (ahi != 0 && chi != 0)
goto OVER_FLOW;
alo = (UInt16)A;
bhi = B >> 16;
blo = (UInt16)B;
clo = (UInt16)C;
tmp = alo * clo;
if (tmp < 0 || SetWide3(this, tmp, alo * bhi, alo * blo))
goto OVER_FLOW;
if (chi != 0)
{ UInt32 Vh = alo * chi;
if (Vh >> 15)
goto OVER_FLOW;
if (((this->fHi >> 16) + (UInt16)Vh) >> 15)
goto OVER_FLOW;
this->fHi += Vh << 16;
}
else // ahi != 0 && chi == 0
{ hsWide w;
UInt32 Vh = ahi * clo;
if (Vh >> 16)
goto OVER_FLOW;
tmp = ahi * bhi;
if (tmp < 0 || SetWide3(&w, tmp, ahi * blo, 0))
goto OVER_FLOW;
if (((w.fHi >> 16) + (UInt16)Vh) >> 15)
goto OVER_FLOW;
w.fHi += Vh << 16;
this->Add(&w);
}
if (neg)
this->Negate();
return this;
OVER_FLOW:
*this = neg ? kNegInfinity64 : kPosInfinity64;
return this;
}
hsWide* hsWide::Div(Int32 denom)
{
if (denom == 0)
{ if (this->IsNeg())
{ hsSignalMathUnderflow();
*this = kNegInfinity64;
}
else
{ hsSignalMathOverflow();
*this = kPosInfinity64;
}
return this;
}
int neg = 0;
Int32 resultH = 0;
UInt32 resultL = 0;
Int32 numerH = this->fHi;
UInt32 numerL = this->fLo;
if (denom < 0)
{ denom = -denom;
neg = ~0;
}
if (WIDE_ISNEG(numerH, numerL))
{ WIDE_NEGATE(numerH, numerL);
neg = ~neg;
}
WIDE_ADDPOS(numerH, numerL, denom >> 1); // add denom/2 to get a round result
UInt32 curr = (UInt32)numerH >> 31;
for (int i = 0; i < 64; i++)
{
WIDE_SHIFTLEFT(resultH, resultL, resultH, resultL, 1);
if (UInt32(denom) <= curr)
{
resultL |= 1;
curr -= denom;
}
WIDE_SHIFTLEFT(numerH, numerL, numerH, numerL, 1);
curr = (curr << 1) | ((UInt32)numerH >> 31);
}
if (neg)
WIDE_NEGATE(resultH, resultL);
return this->Set(resultH, resultL);
}
hsWide* hsWide::Div(const hsWide* denom)
{
hsWide d = *denom;
int shift = 0;
while (d.IsWide())
{ (void)d.ShiftRight(1);
shift += 1;
}
if (shift)
{ d = *denom;
(void)this->RoundRight(shift);
(void)d.RoundRight(shift);
}
return this->Div(d.AsLong());
}
inline int MaxLeftShift(const hsWide* w)
{
Int32 hi = w->fHi;
if (hi == 0)
return 31;
else
{ int shift = -1;
if (hi < 0) hi = -hi;
do {
hi <<= 1;
shift += 1;
} while (hi > 0);
return shift;
}
}
hsFixed hsWide::FixDiv(const hsWide* denom) const
{
hsWide num = *this;
hsWide den = *denom;
int maxShift = MaxLeftShift(this);
if (maxShift >= 16) // easy case
(void)num.ShiftLeft(16);
else
{ (void)num.ShiftLeft(maxShift);
(void)den.RoundRight(16 - maxShift);
}
return num.Div(&den)->AsLong();
}
hsFract hsWide::FracDiv(const hsWide* denom) const
{
hsWide num = *this;
hsWide den = *denom;
int maxShift = MaxLeftShift(this);
if (maxShift >= 30) // easy case
(void)num.ShiftLeft(30);
else
{ (void)num.ShiftLeft(maxShift);
(void)den.RoundRight(30 - maxShift);
}
return num.Div(&den)->AsLong();
}
////////////////////////////////////////////////////////////////////////////////////
Int32 hsWide::Sqrt() const
{
int bits = 32;
UInt32 root = 0;
UInt32 valueH = (UInt32)fHi;
UInt32 valueL = fLo;
UInt32 currH = 0;
UInt32 currL = 0;
UInt32 guessH, guessL;
do {
WIDE_SHIFTLEFT(currH, currL, currH, currL, 2);
currL |= TOP2BITS(valueH);
WIDE_SHIFTLEFT(valueH, valueL, valueH, valueL, 2);
WIDE_SHIFTLEFT(guessH, guessL, 0, root, 2);
root <<= 1;
if (WIDE_LESSTHAN(guessH, guessL, currH, currL))
{ WIDE_ADDPOS(guessH, guessL, 1);
WIDE_SUBWIDE(currH, currL, guessH, guessL);
root |= 1;
}
} while (--bits);
#if HS_PIN_MATH_OVERFLOW
if ((Int32)root < 0)
return kPosInfinity32;
#endif
return (Int32)root;
}
Int32 hsWide::CubeRoot() const
{
int bits = 21;
UInt32 root = 0;
UInt32 valueH = (UInt32)fHi;
UInt32 valueL = fLo;
UInt32 currH, currL;
UInt32 guessH, guessL;
hsBool neg = false;
if (WIDE_ISNEG(valueH, valueL))
{ neg = true;
WIDE_NEGATE(valueH, valueL);
}
currH = currL = 0;
WIDE_SHIFTLEFT(valueH, valueL, valueH, valueL, 1);
do {
WIDE_SHIFTLEFT(currH, currL, currH, currL, 3);
currL |= TOP3BITS(valueH);
WIDE_SHIFTLEFT(valueH, valueL, valueH, valueL, 3);
root <<= 1;
hsWide w;
w.Mul(root, root)->Add(root);
#if 0
w.Mul(3);
#else
hsWide w2 = w;
w.ShiftLeft(1)->Add(&w2);
#endif
guessH = (UInt32)w.fHi;
guessL = w.fLo;
if (WIDE_LESSTHAN(guessH, guessL, currH, currL))
{ WIDE_ADDPOS(guessH, guessL, 1);
WIDE_SUBWIDE(currH, currL, guessH, guessL);
root |= 1;
}
} while (--bits);
if (neg)
root = -Int32(root);
return (Int32)root;
}