michael@0: michael@0: /* michael@0: * Copyright 2008 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkFloat.h" michael@0: #include "SkMathPriv.h" michael@0: michael@0: #define EXP_BIAS (127+23) michael@0: michael@0: static int get_unsigned_exp(uint32_t packed) michael@0: { michael@0: return (packed << 1 >> 24); michael@0: } michael@0: michael@0: static unsigned get_unsigned_value(uint32_t packed) michael@0: { michael@0: return (packed << 9 >> 9) | (1 << 23); michael@0: } michael@0: michael@0: static int get_signed_value(int32_t packed) michael@0: { michael@0: return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed)); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: michael@0: int SkFloat::GetShift(int32_t packed, int shift) michael@0: { michael@0: if (packed == 0) michael@0: return 0; michael@0: michael@0: int exp = get_unsigned_exp(packed) - EXP_BIAS - shift; michael@0: int value = get_unsigned_value(packed); michael@0: michael@0: if (exp >= 0) michael@0: { michael@0: if (exp > 8) // overflow michael@0: value = SK_MaxS32; michael@0: else michael@0: value <<= exp; michael@0: } michael@0: else michael@0: { michael@0: exp = -exp; michael@0: if (exp > 23) // underflow michael@0: value = 0; michael@0: else michael@0: value >>= exp; michael@0: } michael@0: return SkApplySign(value, SkExtractSign(packed)); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: int32_t SkFloat::SetShift(int value, int shift) michael@0: { michael@0: if (value == 0) michael@0: return 0; michael@0: michael@0: // record the sign and make value positive michael@0: int sign = SkExtractSign(value); michael@0: value = SkApplySign(value, sign); michael@0: michael@0: if (value >> 24) // value is too big (has more than 24 bits set) michael@0: { michael@0: int bias = 8 - SkCLZ(value); michael@0: SkASSERT(bias > 0 && bias < 8); michael@0: value >>= bias; michael@0: shift += bias; michael@0: } michael@0: else michael@0: { michael@0: int zeros = SkCLZ(value << 8); michael@0: SkASSERT(zeros >= 0 && zeros <= 23); michael@0: value <<= zeros; michael@0: shift -= zeros; michael@0: } michael@0: // now value is left-aligned to 24 bits michael@0: SkASSERT((value >> 23) == 1); michael@0: michael@0: shift += EXP_BIAS; michael@0: if (shift < 0) // underflow michael@0: return 0; michael@0: else michael@0: { michael@0: if (shift > 255) // overflow michael@0: { michael@0: shift = 255; michael@0: value = 0x00FFFFFF; michael@0: } michael@0: int32_t packed = sign << 31; // set the sign-bit michael@0: packed |= shift << 23; // store the packed exponent michael@0: packed |= ((unsigned)(value << 9) >> 9); // clear 24th bit of value (its implied) michael@0: michael@0: #ifdef SK_DEBUG michael@0: { michael@0: int n; michael@0: michael@0: n = SkExtractSign(packed); michael@0: SkASSERT(n == sign); michael@0: n = get_unsigned_exp(packed); michael@0: SkASSERT(n == shift); michael@0: n = get_unsigned_value(packed); michael@0: SkASSERT(n == value); michael@0: } michael@0: #endif michael@0: return packed; michael@0: } michael@0: } michael@0: michael@0: int32_t SkFloat::Neg(int32_t packed) michael@0: { michael@0: if (packed) michael@0: packed = packed ^ (1 << 31); michael@0: return packed; michael@0: } michael@0: michael@0: int32_t SkFloat::Add(int32_t packed_a, int32_t packed_b) michael@0: { michael@0: if (packed_a == 0) michael@0: return packed_b; michael@0: if (packed_b == 0) michael@0: return packed_a; michael@0: michael@0: int exp_a = get_unsigned_exp(packed_a); michael@0: int exp_b = get_unsigned_exp(packed_b); michael@0: int exp_diff = exp_a - exp_b; michael@0: michael@0: int shift_a = 0, shift_b = 0; michael@0: int exp; michael@0: michael@0: if (exp_diff >= 0) michael@0: { michael@0: if (exp_diff > 24) // B is too small to contribute michael@0: return packed_a; michael@0: shift_b = exp_diff; michael@0: exp = exp_a; michael@0: } michael@0: else michael@0: { michael@0: exp_diff = -exp_diff; michael@0: if (exp_diff > 24) // A is too small to contribute michael@0: return packed_b; michael@0: shift_a = exp_diff; michael@0: exp = exp_b; michael@0: } michael@0: michael@0: int value_a = get_signed_value(packed_a) >> shift_a; michael@0: int value_b = get_signed_value(packed_b) >> shift_b; michael@0: michael@0: return SkFloat::SetShift(value_a + value_b, exp - EXP_BIAS); michael@0: } michael@0: michael@0: static inline int32_t mul24(int32_t a, int32_t b) { michael@0: int64_t tmp = (sk_64_mul(a, b) + (1 << 23)) >> 24; michael@0: return sk_64_asS32(tmp); michael@0: } michael@0: michael@0: int32_t SkFloat::Mul(int32_t packed_a, int32_t packed_b) michael@0: { michael@0: if (packed_a == 0 || packed_b == 0) michael@0: return 0; michael@0: michael@0: int exp_a = get_unsigned_exp(packed_a); michael@0: int exp_b = get_unsigned_exp(packed_b); michael@0: michael@0: int value_a = get_signed_value(packed_a); michael@0: int value_b = get_signed_value(packed_b); michael@0: michael@0: return SkFloat::SetShift(mul24(value_a, value_b), exp_a + exp_b - 2*EXP_BIAS + 24); michael@0: } michael@0: michael@0: int32_t SkFloat::MulInt(int32_t packed, int n) michael@0: { michael@0: return Mul(packed, SetShift(n, 0)); michael@0: } michael@0: michael@0: int32_t SkFloat::Div(int32_t packed_n, int32_t packed_d) michael@0: { michael@0: SkASSERT(packed_d != 0); michael@0: michael@0: if (packed_n == 0) michael@0: return 0; michael@0: michael@0: int exp_n = get_unsigned_exp(packed_n); michael@0: int exp_d = get_unsigned_exp(packed_d); michael@0: michael@0: int value_n = get_signed_value(packed_n); michael@0: int value_d = get_signed_value(packed_d); michael@0: michael@0: return SkFloat::SetShift(SkDivBits(value_n, value_d, 24), exp_n - exp_d - 24); michael@0: } michael@0: michael@0: int32_t SkFloat::DivInt(int32_t packed, int n) michael@0: { michael@0: return Div(packed, SetShift(n, 0)); michael@0: } michael@0: michael@0: int32_t SkFloat::Invert(int32_t packed) michael@0: { michael@0: return Div(packed, SetShift(1, 0)); michael@0: } michael@0: michael@0: int32_t SkFloat::Sqrt(int32_t packed) michael@0: { michael@0: if (packed < 0) michael@0: { michael@0: SkDEBUGFAIL("can't sqrt a negative number"); michael@0: return 0; michael@0: } michael@0: michael@0: int exp = get_unsigned_exp(packed); michael@0: int value = get_unsigned_value(packed); michael@0: michael@0: int nexp = exp - EXP_BIAS; michael@0: int root = SkSqrtBits(value << (nexp & 1), 26); michael@0: nexp >>= 1; michael@0: return SkFloat::SetShift(root, nexp - 11); michael@0: } michael@0: michael@0: #if defined _WIN32 && _MSC_VER >= 1300 // disable warning : unreachable code michael@0: #pragma warning ( push ) michael@0: #pragma warning ( disable : 4702 ) michael@0: #endif michael@0: michael@0: int32_t SkFloat::CubeRoot(int32_t packed) michael@0: { michael@0: sk_throw(); michael@0: return 0; michael@0: } michael@0: michael@0: #if defined _WIN32 && _MSC_VER >= 1300 michael@0: #pragma warning ( pop ) michael@0: #endif michael@0: michael@0: static inline int32_t clear_high_bit(int32_t n) michael@0: { michael@0: return ((uint32_t)(n << 1)) >> 1; michael@0: } michael@0: michael@0: static inline int int_sign(int32_t a, int32_t b) michael@0: { michael@0: return a > b ? 1 : (a < b ? -1 : 0); michael@0: } michael@0: michael@0: int SkFloat::Cmp(int32_t packed_a, int32_t packed_b) michael@0: { michael@0: packed_a = SkApplySign(clear_high_bit(packed_a), SkExtractSign(packed_a)); michael@0: packed_b = SkApplySign(clear_high_bit(packed_b), SkExtractSign(packed_b)); michael@0: michael@0: return int_sign(packed_a, packed_b); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////////////// michael@0: ///////////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_DEBUG michael@0: michael@0: #include "SkRandom.h" michael@0: #include "SkFloatingPoint.h" michael@0: michael@0: void SkFloat::UnitTest() michael@0: { michael@0: #if 0 // def SK_SUPPORT_UNITTEST michael@0: SkFloat a, b, c, d; michael@0: int n; michael@0: michael@0: a.setZero(); michael@0: n = a.getInt(); michael@0: SkASSERT(n == 0); michael@0: michael@0: b.setInt(5); michael@0: n = b.getInt(); michael@0: SkASSERT(n == 5); michael@0: michael@0: c.setInt(-3); michael@0: n = c.getInt(); michael@0: SkASSERT(n == -3); michael@0: michael@0: d.setAdd(c, b); michael@0: SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt()); michael@0: michael@0: SkRandom rand; michael@0: michael@0: int i; michael@0: for (i = 0; i < 1000; i++) michael@0: { michael@0: float fa, fb; michael@0: int aa = rand.nextS() >> 14; michael@0: int bb = rand.nextS() >> 14; michael@0: a.setInt(aa); michael@0: b.setInt(bb); michael@0: SkASSERT(a.getInt() == aa); michael@0: SkASSERT(b.getInt() == bb); michael@0: michael@0: c.setAdd(a, b); michael@0: int cc = c.getInt(); michael@0: SkASSERT(cc == aa + bb); michael@0: michael@0: c.setSub(a, b); michael@0: cc = c.getInt(); michael@0: SkASSERT(cc == aa - bb); michael@0: michael@0: aa >>= 5; michael@0: bb >>= 5; michael@0: a.setInt(aa); michael@0: b.setInt(bb); michael@0: c.setMul(a, b); michael@0: cc = c.getInt(); michael@0: SkASSERT(cc == aa * bb); michael@0: ///////////////////////////////////// michael@0: michael@0: aa = rand.nextS() >> 11; michael@0: a.setFixed(aa); michael@0: cc = a.getFixed(); michael@0: SkASSERT(aa == cc); michael@0: michael@0: bb = rand.nextS() >> 11; michael@0: b.setFixed(bb); michael@0: cc = b.getFixed(); michael@0: SkASSERT(bb == cc); michael@0: michael@0: cc = SkFixedMul(aa, bb); michael@0: c.setMul(a, b); michael@0: SkFixed dd = c.getFixed(); michael@0: int diff = cc - dd; michael@0: SkASSERT(SkAbs32(diff) <= 1); michael@0: michael@0: fa = (float)aa / 65536.0f; michael@0: fb = (float)bb / 65536.0f; michael@0: a.assertEquals(fa); michael@0: b.assertEquals(fb); michael@0: fa = a.getFloat(); michael@0: fb = b.getFloat(); michael@0: michael@0: c.assertEquals(fa * fb, 1); michael@0: michael@0: c.setDiv(a, b); michael@0: cc = SkFixedDiv(aa, bb); michael@0: dd = c.getFixed(); michael@0: diff = cc - dd; michael@0: SkASSERT(SkAbs32(diff) <= 3); michael@0: michael@0: c.assertEquals(fa / fb, 1); michael@0: michael@0: SkASSERT((aa == bb) == (a == b)); michael@0: SkASSERT((aa != bb) == (a != b)); michael@0: SkASSERT((aa < bb) == (a < b)); michael@0: SkASSERT((aa <= bb) == (a <= b)); michael@0: SkASSERT((aa > bb) == (a > b)); michael@0: SkASSERT((aa >= bb) == (a >= b)); michael@0: michael@0: if (aa < 0) michael@0: { michael@0: aa = -aa; michael@0: fa = -fa; michael@0: } michael@0: a.setFixed(aa); michael@0: c.setSqrt(a); michael@0: cc = SkFixedSqrt(aa); michael@0: dd = c.getFixed(); michael@0: SkASSERT(dd == cc); michael@0: michael@0: c.assertEquals(sk_float_sqrt(fa), 2); michael@0: michael@0: // cuberoot michael@0: #if 0 michael@0: a.setInt(1); michael@0: a.cubeRoot(); michael@0: a.assertEquals(1.0f, 0); michael@0: a.setInt(8); michael@0: a.cubeRoot(); michael@0: a.assertEquals(2.0f, 0); michael@0: a.setInt(27); michael@0: a.cubeRoot(); michael@0: a.assertEquals(3.0f, 0); michael@0: #endif michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #endif