blob: 504c1d3f3e183eeb0dc68ab2b078d2503029a702 [file] [log] [blame]
/*
* Copyright (C) 2006-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SkFloat.h"
#include "SkMath.h"
#define EXP_BIAS (127+23)
static int get_unsigned_exp(uint32_t packed)
{
return (packed << 1 >> 24);
}
static unsigned get_unsigned_value(uint32_t packed)
{
return (packed << 9 >> 9) | (1 << 23);
}
static int get_signed_value(int32_t packed)
{
return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed));
}
/////////////////////////////////////////////////////////////////////////
int SkFloat::GetShift(int32_t packed, int shift)
{
if (packed == 0)
return 0;
int exp = get_unsigned_exp(packed) - EXP_BIAS - shift;
int value = get_unsigned_value(packed);
if (exp >= 0)
{
if (exp > 8) // overflow
value = SK_MaxS32;
else
value <<= exp;
}
else
{
exp = -exp;
if (exp > 23) // underflow
value = 0;
else
value >>= exp;
}
return SkApplySign(value, SkExtractSign(packed));
}
/////////////////////////////////////////////////////////////////////////////////////
int32_t SkFloat::SetShift(int value, int shift)
{
if (value == 0)
return 0;
// record the sign and make value positive
int sign = SkExtractSign(value);
value = SkApplySign(value, sign);
if (value >> 24) // value is too big (has more than 24 bits set)
{
int bias = 8 - SkCLZ(value);
SkASSERT(bias > 0 && bias < 8);
value >>= bias;
shift += bias;
}
else
{
int zeros = SkCLZ(value << 8);
SkASSERT(zeros >= 0 && zeros <= 23);
value <<= zeros;
shift -= zeros;
}
// now value is left-aligned to 24 bits
SkASSERT((value >> 23) == 1);
shift += EXP_BIAS;
if (shift < 0) // underflow
return 0;
else
{
if (shift > 255) // overflow
{
shift = 255;
value = 0x00FFFFFF;
}
int32_t packed = sign << 31; // set the sign-bit
packed |= shift << 23; // store the packed exponent
packed |= ((unsigned)(value << 9) >> 9); // clear 24th bit of value (its implied)
#ifdef SK_DEBUG
{
int n;
n = SkExtractSign(packed);
SkASSERT(n == sign);
n = get_unsigned_exp(packed);
SkASSERT(n == shift);
n = get_unsigned_value(packed);
SkASSERT(n == value);
}
#endif
return packed;
}
}
int32_t SkFloat::Neg(int32_t packed)
{
if (packed)
packed = packed ^ (1 << 31);
return packed;
}
int32_t SkFloat::Add(int32_t packed_a, int32_t packed_b)
{
if (packed_a == 0)
return packed_b;
if (packed_b == 0)
return packed_a;
int exp_a = get_unsigned_exp(packed_a);
int exp_b = get_unsigned_exp(packed_b);
int exp_diff = exp_a - exp_b;
int shift_a = 0, shift_b = 0;
int exp;
if (exp_diff >= 0)
{
if (exp_diff > 24) // B is too small to contribute
return packed_a;
shift_b = exp_diff;
exp = exp_a;
}
else
{
exp_diff = -exp_diff;
if (exp_diff > 24) // A is too small to contribute
return packed_b;
shift_a = exp_diff;
exp = exp_b;
}
int value_a = get_signed_value(packed_a) >> shift_a;
int value_b = get_signed_value(packed_b) >> shift_b;
return SkFloat::SetShift(value_a + value_b, exp - EXP_BIAS);
}
#include "Sk64.h"
static inline int32_t mul24(int32_t a, int32_t b)
{
Sk64 tmp;
tmp.setMul(a, b);
tmp.roundRight(24);
return tmp.get32();
}
int32_t SkFloat::Mul(int32_t packed_a, int32_t packed_b)
{
if (packed_a == 0 || packed_b == 0)
return 0;
int exp_a = get_unsigned_exp(packed_a);
int exp_b = get_unsigned_exp(packed_b);
int value_a = get_signed_value(packed_a);
int value_b = get_signed_value(packed_b);
return SkFloat::SetShift(mul24(value_a, value_b), exp_a + exp_b - 2*EXP_BIAS + 24);
}
int32_t SkFloat::MulInt(int32_t packed, int n)
{
return Mul(packed, SetShift(n, 0));
}
int32_t SkFloat::Div(int32_t packed_n, int32_t packed_d)
{
SkASSERT(packed_d != 0);
if (packed_n == 0)
return 0;
int exp_n = get_unsigned_exp(packed_n);
int exp_d = get_unsigned_exp(packed_d);
int value_n = get_signed_value(packed_n);
int value_d = get_signed_value(packed_d);
return SkFloat::SetShift(SkDivBits(value_n, value_d, 24), exp_n - exp_d - 24);
}
int32_t SkFloat::DivInt(int32_t packed, int n)
{
return Div(packed, SetShift(n, 0));
}
int32_t SkFloat::Invert(int32_t packed)
{
return Div(packed, SetShift(1, 0));
}
int32_t SkFloat::Sqrt(int32_t packed)
{
if (packed < 0)
{
SkASSERT(!"can't sqrt a negative number");
return 0;
}
int exp = get_unsigned_exp(packed);
int value = get_unsigned_value(packed);
int nexp = exp - EXP_BIAS;
int root = SkSqrtBits(value << (nexp & 1), 26);
nexp >>= 1;
return SkFloat::SetShift(root, nexp - 11);
}
#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : unreachable code
#pragma warning ( push )
#pragma warning ( disable : 4702 )
#endif
int32_t SkFloat::CubeRoot(int32_t packed)
{
sk_throw();
return 0;
}
#if defined _WIN32 && _MSC_VER >= 1300
#pragma warning ( pop )
#endif
static inline int32_t clear_high_bit(int32_t n)
{
return ((uint32_t)(n << 1)) >> 1;
}
static inline int int_sign(int32_t a, int32_t b)
{
return a > b ? 1 : (a < b ? -1 : 0);
}
int SkFloat::Cmp(int32_t packed_a, int32_t packed_b)
{
packed_a = SkApplySign(clear_high_bit(packed_a), SkExtractSign(packed_a));
packed_b = SkApplySign(clear_high_bit(packed_b), SkExtractSign(packed_b));
return int_sign(packed_a, packed_b);
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
#ifdef SK_DEBUG
#include "SkRandom.h"
#ifdef SK_CAN_USE_FLOAT
#include "SkFloatingPoint.h"
#endif
void SkFloat::UnitTest()
{
#ifdef SK_SUPPORT_UNITTEST
SkFloat a, b, c, d;
int n;
a.setZero();
n = a.getInt();
SkASSERT(n == 0);
b.setInt(5);
n = b.getInt();
SkASSERT(n == 5);
c.setInt(-3);
n = c.getInt();
SkASSERT(n == -3);
d.setAdd(c, b);
SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt());
SkRandom rand;
#ifdef SK_CAN_USE_FLOAT
int i;
for (i = 0; i < 1000; i++)
{
float fa, fb;
int aa = rand.nextS() >> 14;
int bb = rand.nextS() >> 14;
a.setInt(aa);
b.setInt(bb);
SkASSERT(a.getInt() == aa);
SkASSERT(b.getInt() == bb);
c.setAdd(a, b);
int cc = c.getInt();
SkASSERT(cc == aa + bb);
c.setSub(a, b);
cc = c.getInt();
SkASSERT(cc == aa - bb);
aa >>= 5;
bb >>= 5;
a.setInt(aa);
b.setInt(bb);
c.setMul(a, b);
cc = c.getInt();
SkASSERT(cc == aa * bb);
/////////////////////////////////////
aa = rand.nextS() >> 11;
a.setFixed(aa);
cc = a.getFixed();
SkASSERT(aa == cc);
bb = rand.nextS() >> 11;
b.setFixed(bb);
cc = b.getFixed();
SkASSERT(bb == cc);
cc = SkFixedMul(aa, bb);
c.setMul(a, b);
SkFixed dd = c.getFixed();
int diff = cc - dd;
SkASSERT(SkAbs32(diff) <= 1);
fa = (float)aa / 65536.0f;
fb = (float)bb / 65536.0f;
a.assertEquals(fa);
b.assertEquals(fb);
fa = a.getFloat();
fb = b.getFloat();
c.assertEquals(fa * fb, 1);
c.setDiv(a, b);
cc = SkFixedDiv(aa, bb);
dd = c.getFixed();
diff = cc - dd;
SkASSERT(SkAbs32(diff) <= 3);
c.assertEquals(fa / fb, 1);
SkASSERT((aa == bb) == (a == b));
SkASSERT((aa != bb) == (a != b));
SkASSERT((aa < bb) == (a < b));
SkASSERT((aa <= bb) == (a <= b));
SkASSERT((aa > bb) == (a > b));
SkASSERT((aa >= bb) == (a >= b));
if (aa < 0)
{
aa = -aa;
fa = -fa;
}
a.setFixed(aa);
c.setSqrt(a);
cc = SkFixedSqrt(aa);
dd = c.getFixed();
SkASSERT(dd == cc);
c.assertEquals(sk_float_sqrt(fa), 2);
// cuberoot
#if 0
a.setInt(1);
a.cubeRoot();
a.assertEquals(1.0f, 0);
a.setInt(8);
a.cubeRoot();
a.assertEquals(2.0f, 0);
a.setInt(27);
a.cubeRoot();
a.assertEquals(3.0f, 0);
#endif
}
#endif
#endif
}
#endif