| |
| /* |
| * Copyright 2008 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| |
| #include "SkFloat.h" |
| #include "SkMathPriv.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) |
| { |
| SkDEBUGFAIL("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" |
| #include "SkFloatingPoint.h" |
| |
| 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; |
| |
| 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 |