| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ***************************************************************************** |
| * |
| * File: exp_bignum.cpp |
| * Description: Implementation of class BigNum |
| * |
| * |
| * Created: 3/31/99 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ***************************************************************************** |
| */ |
| |
| #include "Platform.h" |
| |
| |
| #include "BigNumHelper.h" |
| #include "exp_bignum.h" |
| #include "Int64.h" |
| #include "str.h" |
| #include "SQLTypeDefs.h" |
| #include "exp_clause_derived.h" |
| |
| #include <iostream> |
| #include <stdlib.h> |
| |
| BigNum::BigNum(Lng32 length, Lng32 precision, short scale, short unSigned) |
| : length_(length), |
| precision_(precision), |
| scale_(scale), |
| unSigned_(unSigned), |
| tempSpacePtr_(0) |
| { |
| setClassID(BigNumID); |
| } |
| |
| BigNum::BigNum() |
| { |
| setClassID(BigNumID); |
| } |
| |
| BigNum::~BigNum() |
| { |
| } |
| |
| Attributes * BigNum::newCopy() |
| { |
| BigNum * new_copy = new BigNum(); |
| *new_copy = *this; |
| return new_copy; |
| }; |
| |
| Attributes * BigNum::newCopy(NAMemory * heap) |
| { |
| BigNum * new_copy = new(heap) BigNum(); |
| *new_copy = *this; |
| return new_copy; |
| }; |
| |
| void BigNum::copyAttrs(Attributes *source) |
| { |
| *this = *((BigNum *) source); |
| return; |
| }; |
| |
| Lng32 BigNum::setTempSpaceInfo(OperatorTypeEnum operType, |
| ULong offset, Lng32 length) |
| { |
| if (length > 0) |
| { |
| // if length is passed in and offset is temp space address. |
| tempSpaceLength_ = length; |
| tempSpacePtr_ = offset; |
| } |
| else |
| if (operType == ITM_DIVIDE || |
| operType == ITM_CAST || |
| operType == ITM_NARROW) // Narrow is another form of CAST |
| { |
| // compute the worst case temp space needed. |
| // See the castFrom(), div(), and BigNumHelper::DivHelper() methods for details. |
| tempSpaceLength_ = 3 * getLength() + 3*sizeof(short) ; // worst case |
| tempSpaceOffset_ = (UInt32) offset; |
| } |
| else |
| { |
| tempSpaceOffset_ = 0; |
| tempSpaceLength_ = 0; |
| } |
| |
| return tempSpaceLength_; |
| } |
| |
| void BigNum::fixup(Space * space, |
| char * constantsArea, |
| char * tempsArea, |
| char * persistentArea, |
| short fixupConstsAndTemps, |
| NABoolean spaceCompOnly) |
| { |
| |
| if ((tempSpaceLength_ > 0) && (NOT spaceCompOnly)) |
| tempSpacePtr_ = (Long)(tempsArea + tempSpaceOffset_); |
| |
| Attributes::fixup(space, constantsArea, tempsArea, persistentArea, |
| fixupConstsAndTemps, spaceCompOnly); |
| |
| } |
| |
| |
| // Convert (i.e. copy) the Big Num from op_data[1] ("source") to op_data[0] ("this"). |
| |
| short BigNum::conv(Attributes * source, char * op_data[]) |
| { |
| return BigNumHelper::ConvBigNumWithSignToBigNumWithSignHelper(((BigNum *) source)->getLength(), |
| getLength(), |
| op_data[1], |
| op_data[0]); |
| } |
| |
| |
| // Compare the Big Num in op_data[1] ("this") with op_data[2] ("other"). |
| // Return 1, if "this <compOp> other" is true,0, otherwise. |
| |
| short BigNum::comp (OperatorTypeEnum compOp, |
| Attributes * other, |
| char * op_data[]) |
| { |
| // Recast char strings as arrays of unsigned shorts |
| unsigned short * thisDataInShorts = (unsigned short *) op_data[1]; |
| unsigned short * otherDataInShorts = (unsigned short *) op_data[2]; |
| |
| // Extract signs. |
| char thisSign = BIGN_GET_SIGN(op_data[1], getLength()); |
| char otherSign = BIGN_GET_SIGN(op_data[2], getLength()); |
| |
| // Clear sign bits |
| BIGN_CLR_SIGN(op_data[1], getLength()); |
| BIGN_CLR_SIGN(op_data[2], getLength()); |
| |
| short returnValue = 0; |
| short compCode = 0; |
| |
| // if the signs are different but 'this' and 'other' values are zeroes, |
| // then they are equal. |
| NABoolean allZeroes = FALSE; |
| if (thisSign != otherSign) { |
| allZeroes = TRUE; |
| // check if the values are zeroes. |
| for (Lng32 i = 0; i < getLength()/2; i++) { |
| // if a nonzero is found, break out. |
| if ((thisDataInShorts[i] != 0) || |
| (otherDataInShorts[i] != 0)) { |
| allZeroes = FALSE; |
| break; |
| } |
| } // for |
| } // if |
| |
| if (allZeroes) |
| compCode = 0; |
| else { |
| // We calculate compCode as follows: if this > other, then compCode = 1 |
| // if this < other, then compCode = -1 |
| // if this = other, then compCode = 0 |
| |
| if ((thisSign == 0) && (otherSign)) // this +ve, other -ve |
| compCode = 1; |
| else |
| if ((thisSign) && (otherSign == 0)) // this -ve, other +ve |
| compCode = -1; |
| else { // both same sign, so check |
| // if magnitude of this is |
| // larger than other |
| |
| for (Lng32 i = 0; i < getLength()/2; i++) { |
| if (otherDataInShorts[i] > thisDataInShorts[i]) |
| compCode = -1; |
| else if (otherDataInShorts[i] < thisDataInShorts[i]) |
| compCode = 1; |
| } |
| |
| if (thisSign) // both -ve, so if magnitudes |
| // are different, switch |
| // compCode |
| compCode = (compCode ? -compCode : 0); |
| } |
| } |
| |
| switch (compOp) { |
| |
| case ITM_EQUAL: |
| returnValue = ((compCode == 0) ? 1 : 0); |
| break; |
| |
| case ITM_NOT_EQUAL: |
| returnValue = ((compCode != 0) ? 1 : 0); |
| break; |
| |
| case ITM_LESS: |
| returnValue = ((compCode < 0) ? 1 : 0); |
| break; |
| |
| case ITM_LESS_EQ: |
| returnValue = ((compCode <= 0) ? 1 : 0); |
| break; |
| |
| case ITM_GREATER: |
| returnValue = ((compCode > 0) ? 1 : 0); |
| break; |
| |
| case ITM_GREATER_EQ: |
| returnValue = ((compCode >= 0) ? 1 : 0); |
| break; |
| |
| } |
| |
| // Reset sign bits |
| if (thisSign) |
| BIGN_SET_SIGN(op_data[1], getLength()); |
| |
| if (otherSign) |
| BIGN_SET_SIGN(op_data[2], getLength()); |
| |
| return returnValue; |
| } |
| |
| |
| // Add the Big Nums in op_data[1] ("left") and op_data[2] ("right") |
| // and store in op_data[0] ("this"). |
| |
| short BigNum::add(Attributes * left, |
| Attributes * right, |
| char * op_data[]) |
| { |
| // Extract signs. |
| char leftSign = BIGN_GET_SIGN(op_data[1], getLength()); |
| char rightSign = BIGN_GET_SIGN(op_data[2], getLength()); |
| |
| // Clear sign bits |
| BIGN_CLR_SIGN(op_data[1], getLength()); |
| BIGN_CLR_SIGN(op_data[2], getLength()); |
| |
| Int32 code; |
| |
| if ((!isUnsigned()) && ((leftSign) || (rightSign))) { |
| // Signed addition. Either left or right is signed. |
| if ((leftSign == 0) && (rightSign)) { |
| code = BigNumHelper::SubHelper(getLength(), op_data[1], op_data[2], op_data[0]); |
| } |
| else |
| if ((leftSign) && (rightSign == 0)) { |
| code = BigNumHelper::SubHelper(getLength(), op_data[2], op_data[1], op_data[0]); |
| } |
| else { |
| // Both left and right are negative numbers |
| BigNumHelper::AddHelper(getLength(), op_data[1], op_data[2], op_data[0]); |
| code = 1; |
| } |
| } |
| else { |
| // Both are positive numbers |
| BigNumHelper::AddHelper(getLength(), op_data[1], op_data[2], op_data[0]); |
| code = 0; |
| } |
| |
| // Reset sign bits |
| if (leftSign) |
| BIGN_SET_SIGN(op_data[1], getLength()); |
| |
| if (rightSign) |
| BIGN_SET_SIGN(op_data[2], getLength()); |
| |
| if (code) { |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| } else { |
| BIGN_CLR_SIGN(op_data[0], getLength()); |
| } |
| |
| return 0; |
| }; |
| |
| |
| // Subtract the Big Num in op_data[2] ("right") from op_data[1] ("left") |
| // and store in op_data[0] ("this"). |
| |
| short BigNum::sub(Attributes * left, |
| Attributes * right, |
| char * op_data[]) |
| { |
| // Extract signs. |
| char leftSign = BIGN_GET_SIGN(op_data[1], getLength()); |
| char rightSign = BIGN_GET_SIGN(op_data[2], getLength()); |
| |
| // Clear sign bits |
| BIGN_CLR_SIGN(op_data[1], getLength()); |
| BIGN_CLR_SIGN(op_data[2], getLength()); |
| |
| Int32 code; |
| |
| if ((!isUnsigned()) && ((leftSign) || (rightSign))) { |
| if (leftSign == 0) { // left - (-right) => left + right |
| BigNumHelper::AddHelper(getLength(), op_data[1], op_data[2], op_data[0]); |
| code = 0; |
| } |
| else |
| if (rightSign == 0) { // -left - right => -(left + right) |
| BigNumHelper::AddHelper(getLength(), op_data[1], op_data[2], op_data[0]); |
| code = 1; |
| } |
| else |
| { // -left - (-right) => right - left |
| code = BigNumHelper::SubHelper(getLength(), op_data[2], op_data[1], op_data[0]); |
| } |
| } |
| else { |
| code = BigNumHelper::SubHelper(getLength(), op_data[1], op_data[2], op_data[0]); |
| } |
| |
| // Reset sign bits |
| if (leftSign) |
| BIGN_SET_SIGN(op_data[1], getLength()); |
| |
| if (rightSign) |
| BIGN_SET_SIGN(op_data[2], getLength()); |
| |
| if (code) { |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| } else { |
| BIGN_CLR_SIGN(op_data[0], getLength()); |
| } |
| |
| return 0; |
| } |
| |
| |
| // Multiply the Big Nums in op_data[1] ("left") and op_data[2] ("right") |
| // and store in op_data[0] ("this"). |
| |
| short BigNum::mul(Attributes * left, |
| Attributes * right, |
| char * op_data[]) |
| { |
| // Extract signs. |
| char leftSign = BIGN_GET_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| char rightSign = BIGN_GET_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| // Clear sign bits |
| BIGN_CLR_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| BIGN_CLR_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| BigNumHelper::MulHelper(getLength(), |
| ((BigNum *) left)->getLength(), |
| ((BigNum *) right)->getLength(), |
| op_data[1], |
| op_data[2], |
| op_data[0]); |
| |
| // Reset sign bits |
| if (leftSign) |
| BIGN_SET_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| |
| if (rightSign) |
| BIGN_SET_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| if (leftSign == rightSign) { |
| BIGN_CLR_SIGN(op_data[0], getLength()); |
| } else { |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| } |
| |
| return 0; |
| } |
| |
| |
| // Divide the Big Num in op_data[1] ("left") by op_data[2] ("right") |
| // and store in op_data[0] ("this"). |
| |
| short BigNum::div(Attributes * left, |
| Attributes * right, |
| char * op_data[], |
| NAMemory *heap, |
| ComDiagsArea** diagsArea) |
| { |
| // Extract signs. |
| // Extract signs. |
| char leftSign = BIGN_GET_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| char rightSign = BIGN_GET_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| // Clear sign bits |
| BIGN_CLR_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| BIGN_CLR_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| // Ignore trailing zeros in right. |
| unsigned short * rightDataInShorts = (unsigned short *) op_data[2]; |
| Lng32 rightLengthInShorts = (((BigNum *) right)->getLength())/2; |
| while ((rightDataInShorts[rightLengthInShorts - 1] == 0) && (rightLengthInShorts > 0)) |
| rightLengthInShorts--; |
| |
| if (rightLengthInShorts == 0) { |
| if (heap) |
| ExRaiseSqlError(heap, diagsArea, EXE_DIVISION_BY_ZERO); |
| |
| // Reset sign bits |
| if (leftSign) |
| BIGN_SET_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| |
| if (rightSign) |
| BIGN_SET_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| return -1; |
| } |
| |
| if (rightLengthInShorts == 1) { |
| BigNumHelper::SimpleDivHelper(((BigNum *) left)->getLength(), |
| 2, |
| op_data[1], |
| op_data[2], |
| op_data[0]); |
| } |
| else { |
| BigNumHelper::DivHelper(((BigNum *) left)->getLength(), |
| 2*rightLengthInShorts, |
| op_data[1], |
| op_data[2], |
| op_data[0], |
| (char *) tempSpacePtr_); |
| } |
| |
| if (leftSign) |
| BIGN_SET_SIGN(op_data[1], ((BigNum*) left)->getLength()); |
| |
| if (rightSign) |
| BIGN_SET_SIGN(op_data[2], ((BigNum*) right)->getLength()); |
| |
| if (leftSign == rightSign) { |
| BIGN_CLR_SIGN(op_data[0], getLength()); |
| } else { |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| } |
| |
| return 0; |
| } |
| |
| |
| // This method converts from other simple types to Big Num |
| |
| short BigNum::castFrom (Attributes * source, |
| char * op_data[], |
| NAMemory *heap, |
| ComDiagsArea** diagsArea) |
| { |
| |
| SimpleType * source1 = (SimpleType *) source; |
| short sourceType = source1->getDatatype(); |
| unsigned short * thisDataInShorts = (unsigned short *) op_data[0]; |
| |
| if ((sourceType >= REC_MIN_INTERVAL) && (sourceType <= REC_MAX_INTERVAL)) { |
| switch (source1->getLength()) { |
| case SQL_SMALL_SIZE: |
| sourceType = REC_BIN16_SIGNED; |
| break; |
| case SQL_INT_SIZE: |
| sourceType = REC_BIN32_SIGNED; |
| break; |
| case SQL_LARGE_SIZE: |
| sourceType = REC_BIN64_SIGNED; |
| break; |
| } |
| } |
| |
| // Initialize magnitude of Big Num to zeros. |
| for (Int32 k = 0; k < getLength(); k++) |
| op_data[0][k] = 0; |
| |
| switch (sourceType) { |
| case REC_BIN16_SIGNED: { |
| if ( *((short *) op_data[1]) < 0) { |
| thisDataInShorts[0] = -*((short *) op_data[1]); |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| } |
| else { |
| thisDataInShorts[0] = *((short *) op_data[1]); |
| } |
| } |
| break; |
| |
| case REC_BPINT_UNSIGNED: |
| case REC_BIN16_UNSIGNED: { |
| thisDataInShorts[0] = *((unsigned short *) op_data[1]); |
| } |
| break; |
| |
| case REC_BIN32_SIGNED: { |
| if ( *((Lng32 *) op_data[1]) < 0) { |
| *((ULng32 *) op_data[0]) = -*((Lng32 *) op_data[1]); |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| } |
| else { |
| *((ULng32 *) op_data[0]) = *((Lng32 *) op_data[1]); |
| } |
| |
| #ifdef NA_LITTLE_ENDIAN |
| // Do nothing, target is already in right format. |
| #else |
| // Reverse the shorts in the target. |
| unsigned short temp = thisDataInShorts[0]; |
| thisDataInShorts[0] = thisDataInShorts[1]; |
| thisDataInShorts[1] = temp; |
| #endif |
| } |
| break; |
| |
| case REC_BIN32_UNSIGNED: { |
| *((ULng32 *) op_data[0]) = *((ULng32 *) op_data[1]); |
| |
| #ifdef NA_LITTLE_ENDIAN |
| // Do nothing, target is already in right format. |
| #else |
| // Reverse the shorts in the target. |
| unsigned short temp = thisDataInShorts[0]; |
| thisDataInShorts[0] = thisDataInShorts[1]; |
| thisDataInShorts[1] = temp; |
| #endif |
| } |
| break; |
| |
| case REC_BIN64_SIGNED: { |
| // Since this case is a little more complex, we call a helper method. |
| BigNumHelper::ConvInt64ToBigNumWithSignHelper(getLength(), |
| *((Int64 *) op_data[1]), |
| op_data[0], |
| FALSE); |
| } |
| break; |
| |
| case REC_BIN64_UNSIGNED: { |
| // Since this case is a little more complex, we call a helper method. |
| BigNumHelper::ConvInt64ToBigNumWithSignHelper(getLength(), |
| *((Int64 *) op_data[1]), |
| op_data[0], |
| TRUE); |
| } |
| break; |
| |
| case REC_DECIMAL_LSE: { |
| // Remember first char of source in temp |
| char temp = op_data[1][0]; |
| |
| // Temporarily suppress sign bit in source |
| op_data[1][0] = op_data[1][0] & 0177; |
| |
| // Convert source from ASCII to Big Num without sign and store in this. |
| BigNumHelper::ConvAsciiToBigNumHelper(source1->getLength(), |
| getLength(), |
| op_data[1], |
| op_data[0]); |
| |
| // Set sign in this (must be done after conversion to prevent overwrite) |
| if (temp & 0200) |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| |
| // Restore first char in source |
| op_data[1][0] = temp; |
| |
| } |
| break; |
| |
| case REC_DECIMAL_UNSIGNED: { |
| // Convert source from ASCII to Big Num without sign and store in this. |
| BigNumHelper::ConvAsciiToBigNumHelper(source1->getLength(), |
| getLength(), |
| op_data[1], |
| op_data[0]); |
| } |
| break; |
| |
| case REC_FLOAT32: { |
| // The code below is modeled on the corresponding code in large decimals. |
| |
| char tempTarget[SQL_REAL_DISPLAY_SIZE + 1]; |
| // Map of tempTarget: -d.dddddddE+dddn |
| // 0123456789012345 |
| // 1 |
| // where '-' is '-' or ' '; '+' is '+' or '-'; n is null character; |
| // and 'd' is a digit in ascii. |
| if (convDoIt(op_data[1], 4, REC_FLOAT32, 0, 0, |
| tempTarget, SQL_REAL_DISPLAY_SIZE, REC_BYTE_F_ASCII, 0, 0, |
| NULL, 0, heap, diagsArea, CONV_UNKNOWN_LEFTPAD) != ex_expr::EXPR_OK) |
| return -1; |
| |
| Lng32 i; |
| |
| // Compute the 8-digit mantissa by skipping the decimal point. |
| ULng32 mantissa = 0; |
| for (i = 1; i <= 9; i++) { |
| if (i != 2) |
| mantissa = mantissa * 10 + (tempTarget[i] - '0'); |
| } |
| |
| // Get the exponent - absolute value only |
| Lng32 exponent = 0; |
| Lng32 multiplier = 100; |
| for (i = 12; i < 15; i++) { |
| exponent += (tempTarget[i] - '0') * multiplier; |
| multiplier /= 10; |
| } |
| |
| // If we're dealing with a positive exponent, then we have at least |
| // "exponent" number of digits before a decimal point followed by "scale" |
| // digits. Make sure that the number of digits to the left of the |
| // decimal point (i.e. precision - scale) is >= "exponent" number of |
| // digits. Special care is taken when the float is less than 1 - in this |
| // case, the 0 digit before the decimal point should not count towards |
| // the precision, since it will be ignored. |
| |
| if (tempTarget[11] != '-') { |
| Int32 includeFirstDigit = (tempTarget[1] == '0') ? 0 : 1; |
| if ((exponent + includeFirstDigit) > (getPrecision() - getScale())) |
| return -1; |
| } |
| |
| if (tempTarget[11] == '-') |
| exponent = -exponent; |
| |
| // Subtract 7 from the exponent. This will set the exponent exactly |
| // where the decimal point should be in the mantissa. |
| exponent -= 7; |
| |
| // Shift down the mantissa so as to leave "scale" digits to the right of |
| // the decimal point. |
| Lng32 scaleBy = getScale() + exponent; |
| if (scaleBy < 0) |
| { |
| Int8 roundMe= 0; |
| for (i = 0; i < -scaleBy; i++) |
| { |
| roundMe = ( mantissa %10 ) > 4? 1:0; |
| mantissa /= 10; |
| } |
| |
| if(roundMe == 1) //round it |
| mantissa++; |
| |
| scaleBy = 0; |
| } |
| |
| // At this stage, our source is effectively = mantissa * 10^exponent. |
| // We need to create the target Big Num by multiplying the mantissa |
| // with 10^(scale + exponent). |
| |
| // Create a Big Num (without sign) for 10^(scale + exponent); |
| // This can be stored in the temporary area pointed to by tempSpacePtr_. |
| char * tempPowersOfTen = (char *) tempSpacePtr_; |
| Lng32 tempPowersOfTenLength; |
| BigNumHelper::ConvPowersOfTenToBigNumHelper(scaleBy, |
| tempSpaceLength_, |
| &tempPowersOfTenLength, |
| tempPowersOfTen); |
| |
| // Convert the mantissa into a Big Num representation (without sign). |
| unsigned short * mantissaDataInShorts = (unsigned short *) &mantissa; |
| #ifdef NA_LITTLE_ENDIAN |
| // Do nothing, mantissaData is already in the correct format. |
| #else |
| // Swap the two shorts in mantissaData. |
| unsigned short temp = mantissaDataInShorts[0]; |
| mantissaDataInShorts[0] = mantissaDataInShorts[1]; |
| mantissaDataInShorts[1] = temp; |
| #endif |
| |
| // Multiply mantissaData with tempPowersOfTen and store in this. |
| BigNumHelper::MulHelper(getLength(), |
| 4, |
| tempPowersOfTenLength, |
| (char *) &mantissa, |
| tempPowersOfTen, |
| op_data[0]); |
| |
| // Set sign of this. |
| if ( tempTarget[0] == '-') |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| |
| } |
| |
| break; |
| case REC_FLOAT64: { |
| |
| // The code below is modeled on the corresponding code in large decimals. |
| |
| char tempTarget[SQL_FLOAT_DISPLAY_SIZE + 1]; |
| // Map of tempTarget: -d.dddddddddddddddddE+dddn |
| // 01234567890123456789012345 |
| // 1 2 |
| // where '-' is '-' or ' '; '+' is '+' or '-'; n is null character; |
| // and 'd' is a digit in ascii. |
| if (convDoIt(op_data[1], 8, REC_FLOAT64, 0, 0, |
| tempTarget, SQL_FLOAT_DISPLAY_SIZE, REC_BYTE_F_ASCII, 0, 0, |
| NULL, 0, heap, diagsArea, CONV_UNKNOWN_LEFTPAD) != ex_expr::EXPR_OK) |
| return -1; |
| |
| Lng32 i; |
| |
| // Compute the 18-digit mantissa by skipping the decimal point. |
| Int64 mantissa = 0; |
| for (i = 1; i <= 19; i++) { |
| if (i != 2) |
| mantissa = mantissa * 10 + (tempTarget[i] - '0'); |
| } |
| |
| // Get the exponent - absolute value only |
| Lng32 exponent = 0; |
| Lng32 multiplier = 100; |
| for (i = 22; i < 25; i++) |
| { |
| exponent += (tempTarget[i] - '0') * multiplier; |
| multiplier /= 10; |
| } |
| |
| // If we're dealing with a positive exponent, then we have at least |
| // "exponent" number of digits before a decimal point followed by "scale" |
| // digits. Make sure that the number of digits to the left of the |
| // decimal point (i.e. precision - scale) is >= "exponent" number of |
| // digits. Special care is taken when the float is less than 1 - in this |
| // case, the 0 digit before the decimal point should not count towards |
| // the precision, since it will be ignored. |
| |
| if (tempTarget[21] != '-') { |
| Int32 includeFirstDigit = (tempTarget[1] == '0') ? 0 : 1; |
| if ((exponent + includeFirstDigit) > (getPrecision() - getScale())) |
| return -1; |
| } |
| |
| if (tempTarget[21] == '-') |
| exponent = -exponent; |
| |
| // Subtract 17 from the exponent. This will set the exponent exactly |
| // where the decimal point should be in the mantissa. |
| exponent -= 17; |
| |
| // Shift down the mantissa so as to leave "scale" digits to the right of |
| // the decimal point. |
| Lng32 scaleBy = getScale() + exponent; |
| if (scaleBy < 0) |
| { |
| Int8 roundMe = 0; |
| for (i = 0; i < -scaleBy; i++) |
| { |
| roundMe = ( mantissa %10 ) > 4? 1:0; |
| mantissa /= 10; |
| } |
| |
| if(roundMe == 1) |
| mantissa++; |
| |
| scaleBy = 0; |
| } |
| |
| // At this stage, our source is effectively = mantissa * 10^exponent. |
| // We need to create the target Big Num by multiplying the mantissa |
| // with 10^(scale + exponent). |
| |
| // Create a Big Num (without sign) for 10^(scale + exponent); |
| // This can be stored in the temporary area pointed to by tempSpacePtr_. |
| char * tempPowersOfTen = (char *) tempSpacePtr_; |
| if (tempSpacePtr_ == 0) |
| { |
| tempPowersOfTen = new(heap) char[tempSpaceLength_]; |
| } |
| Lng32 tempPowersOfTenLength; |
| BigNumHelper::ConvPowersOfTenToBigNumHelper(scaleBy, |
| tempSpaceLength_, |
| &tempPowersOfTenLength, |
| tempPowersOfTen); |
| |
| // Convert the mantissa into a Big Num representation (without sign). |
| unsigned short * mantissaDataInShorts = (unsigned short *) &mantissa; |
| #ifdef NA_LITTLE_ENDIAN |
| // Do nothing, mantissaData is already in the correct format. |
| #else |
| // Reverse the four shorts in mantissaData. |
| unsigned short temp = mantissaDataInShorts[0]; |
| mantissaDataInShorts[0] = mantissaDataInShorts[3]; |
| mantissaDataInShorts[3] = temp; |
| temp = mantissaDataInShorts[1]; |
| mantissaDataInShorts[1] = mantissaDataInShorts[2]; |
| mantissaDataInShorts[2] = temp; |
| #endif |
| |
| // Multiply mantissaData with tempPowersOfTen and store in this. |
| BigNumHelper::MulHelper(getLength(), |
| 8, |
| tempPowersOfTenLength, |
| (char *) &mantissa, |
| tempPowersOfTen, |
| op_data[0]); |
| |
| // Set sign of this. |
| if ( tempTarget[0] == '-') |
| // This originally set sign to 0! |
| //*thisSign = 0; |
| BIGN_SET_SIGN(op_data[0], getLength()); |
| |
| if ((Long)tempPowersOfTen != tempSpacePtr_) |
| NADELETEBASIC(tempPowersOfTen, heap); |
| |
| } |
| break; |
| |
| default: |
| { |
| // Inform the caller that the conversion is not supported. |
| return -1; |
| } |
| break; |
| } |
| |
| return 0; |
| }; |
| |
| |
| void BigNum::encode(const char * inBuf, char * outBuf, short desc) |
| { |
| char sign = BIGN_GET_SIGN(inBuf, getLength()); |
| NABoolean neg = (sign) ? TRUE : FALSE; |
| |
| // Clear sign bits |
| BIGN_CLR_SIGN(inBuf, getLength()); |
| |
| // If the platform is little endian |
| // copy in reverse the bytes of the magnitude |
| // else |
| // copy in reverse the shorts of the magnitude. |
| |
| #ifdef NA_LITTLE_ENDIAN |
| Int32 i = 0; |
| for (i = 0; i < getLength(); i++) |
| outBuf[getLength() - i - 1] = (neg ? ~inBuf[i] : inBuf[i]); |
| #else |
| Int32 i = 0; |
| for (i = 0; i < getLength(); i += 2) { |
| outBuf[getLength() - i - 2] = (neg ? ~inBuf[i] : inBuf[i]); |
| outBuf[getLength() - i - 1] = (neg ? ~inBuf[i+1] : inBuf[i+1]); |
| } |
| #endif |
| |
| // Reset sign |
| if (neg) |
| BIGN_SET_SIGN(inBuf, getLength()); |
| |
| // if positive number, set the leftmost bit. Else zero it out. |
| if (neg) |
| outBuf[0] &= MSB_CLR_MSK; |
| else |
| outBuf[0] |= MSB_SET_MSK; |
| } |
| |
| void BigNum::decode(const char * inBuf, char * outBuf, short desc) |
| { |
| // If MSB set in key, then value is NOT negative. |
| NABoolean neg = !(inBuf[0] & MSB_SET_MSK); |
| |
| // If the platform is little endian |
| // copy in reverse the bytes of the magnitude |
| // else |
| // copy in reverse the shorts of the magnitude. |
| |
| #ifdef NA_LITTLE_ENDIAN |
| Int32 i = 0; |
| for (i = 0; i < getLength(); i++) |
| outBuf[getLength() - i - 1] = (neg ? ~inBuf[i] : inBuf[i]); |
| #else |
| Int32 i = 0; |
| for (i = 0; i < getLength(); i += 2) { |
| outBuf[getLength() - i - 2] = (neg ? ~inBuf[i] : inBuf[i]); |
| outBuf[getLength() - i - 1] = (neg ? ~inBuf[i+1] : inBuf[i+1]); |
| } |
| #endif |
| |
| // If positive, clear out sign bit. If it's negative, the bit would have |
| // already been flipped on during the copy above. |
| if (!neg) |
| BIGN_CLR_SIGN(outBuf, getLength()); |
| } |
| |
| void BigNum::init(char * op_data, char * str) |
| |
| { |
| Lng32 strLength = str_len(str); |
| |
| // Skip ascii sign |
| Int32 skip = ((str[0] == '-') || (str[0] == '+')) ? 1 : 0; |
| |
| // Convert magnitude from ASCII to Big Num without sign. |
| BigNumHelper::ConvAsciiToBigNumHelper(strLength - skip, |
| getLength(), |
| str + skip, |
| op_data); |
| |
| // Set sign bit |
| if (str[0] == '-') |
| BIGN_SET_SIGN(op_data, getLength()); |
| } |
| |