// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "Header Files/CalcEngine.h" using namespace CalcEngine; using namespace CalcEngine::RationalMath; using namespace std; // To be called when either the radix or num width changes. You can use -1 in either of these values to mean // dont change that. void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth) { // When in integer mode, the number is represented in 2's complement form. When a bit width is changing, we can // change the number representation back to sign, abs num form in ratpak. Soon when display sees this, it will // convert to 2's complement form, but this time all high bits will be propagated. Eg. -127, in byte mode is // represented as 1000,0001. This puts it back as sign=-1, 01111111 . But DisplayNum will see this and convert it // back to 1111,1111,1000,0001 when in Word mode. if (m_fIntegerMode) { uint64_t w64Bits = m_currentVal.ToUInt64_t(); bool fMsb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1; // make sure you use the old width if (fMsb) { // If high bit is set, then get the decimal number in -ve 2'scompl form. auto tempResult = m_currentVal ^ m_chopNumbers[m_numwidth]; m_currentVal = -(tempResult + 1); } } if (radixtype >= HEX_RADIX && radixtype <= BIN_RADIX) { m_radix = NRadixFromRadixType(radixtype); // radixtype is not even saved } if (numwidth >= QWORD_WIDTH && numwidth <= BYTE_WIDTH) { m_numwidth = numwidth; m_dwWordBitWidth = DwWordBitWidthFromeNumWidth(numwidth); } // inform ratpak that a change in base or precision has occurred BaseOrPrecisionChanged(); // display the correct number for the new state (ie convert displayed // number to correct base) DisplayNum(); } int32_t CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH /*numwidth*/) { static constexpr int nBitMax[] = { 64, 32, 16, 8 }; int32_t wmax = nBitMax[0]; if (m_numwidth >= 0 && (size_t)m_numwidth < size(nBitMax)) { wmax = nBitMax[m_numwidth]; } return wmax; } uint32_t CCalcEngine::NRadixFromRadixType(RADIX_TYPE radixtype) { static constexpr uint32_t rgnRadish[4] = { 16, 10, 8, 2 }; /* Number bases in the same order as radixtype */ uint32_t radix = 10; // convert special bases into symbolic values if (radixtype >= 0 && (size_t)radixtype < size(rgnRadish)) { radix = rgnRadish[radixtype]; } return radix; } // Toggles a given bit into the number representation. returns true if it changed it actually. bool CCalcEngine::TryToggleBit(CalcEngine::Rational& rat, uint32_t wbitno) { uint32_t wmax = DwWordBitWidthFromeNumWidth(m_numwidth); if (wbitno >= wmax) { return false; // ignore error cant happen } Rational result = Integer(rat); // Remove any variance in how 0 could be represented in rat e.g. -0, 0/n, etc. result = (result != 0 ? result : 0); // XOR the result with 2^wbitno power rat = result ^ Pow(2, static_cast(wbitno)); return true; } // Returns the nearest power of two int CCalcEngine::QuickLog2(int iNum) { int iRes = 0; // while first digit is a zero while (!(iNum & 1)) { iRes++; iNum >>= 1; } // if our number isn't a perfect square iNum = iNum >> 1; if (iNum) { // find the largest digit for (iNum = iNum >> 1; iNum; iNum = iNum >> 1) ++iRes; // and then add two iRes += 2; } return iRes; } //////////////////////////////////////////////////////////////////////// // // UpdateMaxIntDigits // // determine the maximum number of digits needed for the current precision, // word size, and base. This number is conservative towards the small side // such that there may be some extra bits left over. For example, base 8 requires 3 bits per digit. // A word size of 32 bits allows for 10 digits with a remainder of two bits. Bases // that require variable number of bits (non-power-of-two bases) are approximated // by the next highest power-of-two base (again, to be conservative and guarantee // there will be no over flow verse the current word size for numbers entered). // Base 10 is a special case and always uses the base 10 precision (m_nPrecisionSav). void CCalcEngine::UpdateMaxIntDigits() { if (m_radix == 10) { // if in integer mode you still have to honor the max digits you can enter based on bit width if (m_fIntegerMode) { m_cIntDigitsSav = static_cast(m_maxDecimalValueStrings[m_numwidth].length()) - 1; // This is the max digits you can enter a decimal in fixed width mode aka integer mode -1. The last digit // has to be checked separately } else { m_cIntDigitsSav = m_precision; } } else { m_cIntDigitsSav = m_dwWordBitWidth / CCalcEngine::QuickLog2(m_radix); } } void CCalcEngine::ChangeBaseConstants(uint32_t radix, int maxIntDigits, int32_t precision) { if (10 == radix) { ChangeConstants(radix, precision); // Base 10 precision for internal computing still needs to be 32, to // take care of decimals precisely. For eg. to get the HI word of a qword, we do a rsh, which depends on getting // 18446744073709551615 / 4294967296 = 4294967295.9999917... This is important it works this and doesn't reduce // the precision to number of digits allowed to enter. In other words, precision and # of allowed digits to be // entered are different. } else { ChangeConstants(radix, maxIntDigits + 1); } } void CCalcEngine::BaseOrPrecisionChanged() { UpdateMaxIntDigits(); CCalcEngine::ChangeBaseConstants(m_radix, m_cIntDigitsSav, m_precision); }