題目描述:
請編寫程序實現:
兩個任意長度的正數相減,這兩個正數可以帶小數點,也可以是整數,請輸出結果.
系統假設:
輸入的字符串中,不會出現除了數字與小數點以外的其它字符,不會出現多個小數點以及小數點在第一個字符的位置
等非法情況,所以考生的程序中無須考慮輸入的數值字符串非法的情況。
詳細要求以及系統約束
1.輸入均爲正數,但輸出可能爲負數
2.輸入輸出均爲字符串形式,輸入的字符串以“\0”結束,輸出的結果字符串也必須以“\0”結束
3.如果輸出是正數則不需要帶符號,如果爲負數,則輸出的結果字符串需要帶負號
例如:2.2-1.1 直接輸出爲“1.1”,1.1-2.2 則需要輸出爲“-1.1”
4.輸出的結果字符串需要過濾掉整數位前以及小數位後無效的0,小數位爲全0的,直接輸出整數位
例如相減結果爲11.345,此數值前後均不可以帶0,“011.345”或者“0011.34500”等等前後帶無效0的均視爲錯誤
輸出。例如1.1-1.1結果爲0.0,則直接輸出0。
5.輸出的字符串內存由考生的程序負責分配,測試程序負責釋放
本題出自高級篇,該題看起來意思簡單,實則要注意的地方有很多,特別是標紅的地方。簡要說說該題的幾點注意事項:
- 由於長度任意,因此想把字符串轉換爲數字後再計算的想法就被槍斃了;
- 涉及到小數運算,對齊借位的問題更加麻煩;
- 得到結果字符串中“0”的處理。
// 用例1:小數點+前0+負數結果
void CExampleTest::TestCase01()
{
const char * pMinuend = "01.";
const char * pSubtrahend = "2.";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "-1") == 0);
free(pResult);
}
// 用例2:位數測試
void CExampleTest::TestCase02()
{
const char * pMinuend = "8.1";
const char * pSubtrahend = "0.00000000000000000000001";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "8.09999999999999999999999") == 0);
free(pResult);
}
// 用例3:後0
void CExampleTest::TestCase03()
{
const char * pMinuend = "8.5";
const char * pSubtrahend = "7.5";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "1") == 0);
free(pResult);
}
// 用例4:負數+小數點
void CExampleTest::TestCase04()
{
const char * pMinuend = "12.34";
const char * pSubtrahend = "17.24";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "-4.9") == 0);
free(pResult);
}
// 用例5:整數+後0
void CExampleTest::TestCase05()
{
const char * pMinuend = "12.";
const char * pSubtrahend = "2";
char * pResult = NULL;
Decrease(pMinuend, pSubtrahend, &pResult);
CPPUNIT_ASSERT(pResult != NULL);
CPPUNIT_ASSERT(strcmp(pResult, "10") == 0);
free(pResult);
}
根據上述的分析可知,本題的思路爲:
- 先判斷結果是否爲負,若爲負則將標誌位置位,轉換爲大數減小數,最後要將結果加上負號;
- 小數位對齊;
- 從最低位開始相減,不夠則向前借位;
- 進行結果中“0”的處理,去掉整數部分頭部的“0”和小數部分尾部的“0”;
/******************************************************************************
Copyright (C), 2001-2011, Huawei Tech. Co., Ltd.
******************************************************************************
File Name :
Version :
Author :
Created : 2009/10/10
Last Modified :
Description :
Function List :
History :
1.Date : 2009/10/10
Author :
Modification: Created file
******************************************************************************/
#include <stdlib.h>
#include <string.h>
typedef struct MYNUM
{
char *pZhengshu;
char *pXiaoshu;
}MyNum;
/*獲取整數部分和小數部分*/
int GetTwoParts(const char *pcStr,char **pcZhengshu,char **pcXiaoshu)
{
if (NULL == pcStr)
return 0;
char *pZhengshu = NULL;
char *pXiaoshu = NULL;
size_t nLen = strlen(pcStr);
int i = 0;
while ('\0' != pcStr[i])
{
if ('.' == pcStr[i])
break;
i++;
}
i++;
pZhengshu = (char *)malloc(i * sizeof (char));
if (NULL == pZhengshu)
return 0;
strncpy(pZhengshu,pcStr,i - 1);
pZhengshu[i - 1] = '\0';
*pcZhengshu = pZhengshu;
int nXiaoshuLen = 0;
if (nLen <= i)
nXiaoshuLen = 1;
else
nXiaoshuLen = nLen - i + 1;
pXiaoshu = (char *)malloc(nXiaoshuLen * sizeof (char));
if (NULL == pXiaoshu)
return 0;
if (nLen > i)
{
strncpy(pXiaoshu,&pcStr[i],(nLen - i));
}
pXiaoshu[nXiaoshuLen - 1] = '\0';
*pcXiaoshu = pXiaoshu;
return (i - 1);
}
/*獲取整數部分位數*/
size_t GetIntNum(const char *pcStr)
{
size_t i = 0;
size_t nNum = 0;
bool bHeadZero = true;
while ('\0' != pcStr[i])
{
if ('0' != pcStr[i])
bHeadZero = false;
if ('.' == pcStr[i])
break;
if (!bHeadZero)
nNum++;
i++;
}
return nNum;
}
/*正數減法*/
void PosDecrease(const char *pMinuend,const char* pSubtrahend,char **pResult,bool bIsNeg)
{
bool bPointFlag = false;
int nLen1 = strlen(pMinuend);
int nLen2 = strlen(pSubtrahend);
if ('.' == pMinuend[nLen1 - 1])
nLen1--;
if ('.' == pSubtrahend[nLen2 - 1])
nLen2--;
char *pTem = NULL;
char *pMinTem = NULL;
pTem = (char *)malloc((nLen1 + 1) * sizeof (char));
pMinTem = (char*)malloc((nLen1 + 1) * sizeof (char));
if ((NULL == pTem) || (NULL == pMinTem))
return;
strncpy(pMinTem,pMinuend,nLen1);
pMinTem[nLen1] = '\0';
for (int i = 0;i < nLen1;i++)
{
if ('.' == pMinTem[nLen1 - i - 1])
{
pTem[nLen1 - 1 - i] = '.';
bPointFlag = true;
continue;
}
char cSub;
if (i >= nLen2)
cSub = '0';
else
cSub = pSubtrahend[nLen2 - i - 1];
char cTem = pMinTem[nLen1 - i - 1] - cSub;
if (0 > cTem)
{
for (int j = 0;j < nLen1 -i;j++)
{
if ('.' == pMinTem[nLen1 - i - 1 - j -1])
continue;
if ('0' > (pMinTem[nLen1- i - 1 - j - 1]--))
{
pMinTem[nLen1 - i - 1 - j - 1] += 10;
}
else
break;
}
cTem += 10;
}
cTem += '0';
pTem[nLen1 - 1 - i] = cTem;
}
pTem[nLen1] = '\0';
int nStart,nEnd;
for (nStart= 0;nStart< nLen1;nStart++)
{
if ('0' != pTem[nStart])
break;
}
for (nEnd = nLen1 - 1;nEnd >= 0;nEnd--)
{
if ('0' != pTem[nEnd])
break;
}
if ('.' == pTem[nStart])
nStart--;
if ('.' == pTem[nEnd])
nEnd--;
if (!bPointFlag)
nEnd = nLen1 - 1;
char *pLast = NULL;
if (nLen1 == nStart)
{
pLast = (char *)malloc(2 * sizeof (char));
if (NULL == pLast)
return;
strcpy(pLast,"0");
}
else
{
int nLen = nEnd - nStart + 1;
if (bIsNeg)
{
pLast = (char *)malloc((nLen + 2) * sizeof(char));
if (NULL == pLast)
return;
pLast[0] = '-';
strncpy(&pLast[1],&pTem[nStart],nLen);
pLast[nLen + 1] = '\0';
}
else
{
pLast = (char *)malloc((nLen+ 1) * sizeof(char));
strncpy(pLast,&pTem[nStart],nLen);
pLast[nLen] = '\0';
}
}
*pResult = pLast;
free(pTem);
free(pMinTem);
}
/**********************************************************
描述: 小數位對齊
輸入:
const char *pStr 需要對齊的字符串
int nAlignLen 對齊後的位數
輸出:
char **pAlign 對齊後的字符串,以'\0'結束
返回值:
0 成功
-1 失敗
***********************************************************/
int XiaoshuAlign(const char *pStr,int nAlignLen,char **pAlign)
{
if (NULL == pStr)
return -1;
int nLen = strlen(pStr);
if (nLen > nAlignLen)
return -1;
bool bPointFlag = false;
int i = 0;
while ('\0' != pStr[i])
{
if ('.' == pStr[i])
{
bPointFlag = true;
break;
}
i++;
}
if (!bPointFlag)
nAlignLen++;
char *pAlignTem = NULL;
pAlignTem = (char*)malloc((nAlignLen + 1) * sizeof(char));
if (NULL == pAlignTem)
return -1;
strcpy(pAlignTem,pStr);
for (int i = nLen;i < nAlignLen;i++)
{
if (!bPointFlag && (i == nLen))
pAlignTem[i] = '.';
else
pAlignTem[i] = '0';
}
pAlignTem[nAlignLen] = '\0';
*pAlign = pAlignTem;
return 0;
}
/*****************************************************************************
Description : 兩個任意長度的正數相減
Prototype : int Decrease(const char *pMinuend, const char *pSubtrahend, char **ppResult)
Input Param : const char *pMinuend 被減數,以\0表示字符串結束
const char *pSubtrahend 減數,以\0表示字符串結束
Output : char **ppResult 減法結果,必須以\0表示字符串結束
Return Value : 成功返回0 失敗返回-1
*****************************************************************************/
int Decrease(const char *pMinuend, const char *pSubtrahend, char **ppResult)
{
/* 在這裏實現功能 */
if ((NULL == pMinuend) || (NULL == pSubtrahend))
return -1;
char *pResult = NULL;
size_t n1,n2;
n1 = n2 = 0;
n1 = GetIntNum(pMinuend);
n2 = GetIntNum(pSubtrahend);
const char *cVar1 = NULL;
const char *cVar2 = NULL;
bool bIsNeg = false;
if (n1 > n2)
{
cVar1 = pMinuend;
cVar2 = pSubtrahend;
}
else if (n1 == n2)
{
if (0 <= strcmp(pMinuend,pSubtrahend))
{
cVar1 = pMinuend;
cVar2 = pSubtrahend;
}
else
{
cVar1 = pSubtrahend;
cVar2 = pMinuend;
bIsNeg = true;
}
}
else
{
cVar1 = pSubtrahend;
cVar2 = pMinuend;
bIsNeg = true;
}
size_t nLen1 = strlen(cVar1);
size_t nLen2 = strlen(cVar2);
MyNum m_Num1,m_Num2;
m_Num1.pZhengshu = m_Num1.pXiaoshu = m_Num2.pZhengshu = m_Num2.pXiaoshu = NULL;
GetTwoParts(cVar1,&(m_Num1.pZhengshu), &(m_Num1.pXiaoshu));
GetTwoParts(cVar2,&(m_Num2.pZhengshu),&(m_Num2.pXiaoshu));
int nXiaoshuLen1 = strlen(m_Num1.pXiaoshu);
int nXiaoshuLen2 = strlen(m_Num2.pXiaoshu);
if (nXiaoshuLen1 < nXiaoshuLen2)
{
char *pAlignStr = NULL;
if (0 != XiaoshuAlign(cVar1,nLen1 + nXiaoshuLen2 - nXiaoshuLen1,&pAlignStr))
return -1;
PosDecrease(pAlignStr,cVar2,&pResult,bIsNeg);
}
else
{
char *pAlignStr = NULL;
if (0 != XiaoshuAlign(cVar2,nLen2 + nXiaoshuLen1 - nXiaoshuLen2,&pAlignStr))
return -1;
PosDecrease(cVar1,pAlignStr,&pResult,bIsNeg);
}
if (NULL == pResult)
return -1;
*ppResult = pResult;
if (0 != strcmp("",m_Num1.pZhengshu))
{
free(m_Num1.pZhengshu);
m_Num1.pZhengshu = NULL;
}
if (0 != strcmp("",m_Num1.pXiaoshu))
{
free(m_Num1.pXiaoshu);
m_Num1.pZhengshu = NULL;
}
if (0 != strcmp("",m_Num2.pZhengshu))
{
free(m_Num2.pZhengshu);
m_Num2.pZhengshu = NULL;
}
if (0 != strcmp("",m_Num2.pXiaoshu))
{
free(m_Num2.pXiaoshu);
m_Num2.pXiaoshu = NULL;
}
return 0;
}
整體思路和上面是對應的,具體實現可能小的地方沒有完善好。在提交、修改七八次後,終於全部通過了所有用例。
尾記:第一遍提交的時候,結果爲“運行時間超限”,查找之後發現是,strcpy函數用法錯誤的問題。後來自己測用例的時候出現了CRT detected that the application wrote to memory after end of heap buffer的問題,調試後定位到錯誤的原因是調用free函數時出錯,找找後發現還是分配內存後,使用strcpy的沒注意到字符串長度問題,解決的辦法是用strncpy,比較安全。