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
338 lines
6.9 KiB
14 years ago
|
/*==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;
|
||
|
}
|
||
|
|