This closes #1031, fix small float parsed error in some case

- new formula function: YEARFRAC, ref #65
- update the codecov version
- remove unused variable
This commit is contained in:
xuri 2021-10-15 21:45:46 +08:00
parent 58fd279dc8
commit de38402f74
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
7 changed files with 313 additions and 78 deletions

View File

@ -31,7 +31,7 @@ jobs:
run: env GO111MODULE=on go test -v -timeout 30m -race ./... -coverprofile=coverage.txt -covermode=atomic run: env GO111MODULE=on go test -v -timeout 30m -race ./... -coverprofile=coverage.txt -covermode=atomic
- name: Codecov - name: Codecov
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v2
with: with:
file: coverage.txt file: coverage.txt
flags: unittests flags: unittests

161
calc.go
View File

@ -55,13 +55,6 @@ const (
criteriaG criteriaG
criteriaErr criteriaErr
criteriaRegexp criteriaRegexp
// Numeric precision correct numeric values as legacy Excel application
// https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel In the
// top figure the fraction 1/9000 in Excel is displayed. Although this number
// has a decimal representation that is an infinite string of ones, Excel
// displays only the leading 15 figures. In the second line, the number one
// is added to the fraction, and again Excel displays only 15 figures.
numericPrecision = 1000000000000000
maxFinancialIterations = 128 maxFinancialIterations = 128
financialPercision = 1.0e-08 financialPercision = 1.0e-08
// Date and time format regular expressions // Date and time format regular expressions
@ -511,6 +504,7 @@ type formulaFuncs struct {
// WEIBULL.DIST // WEIBULL.DIST
// XOR // XOR
// YEAR // YEAR
// YEARFRAC
// Z.TEST // Z.TEST
// ZTEST // ZTEST
// //
@ -533,7 +527,7 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
result = token.TValue result = token.TValue
isNum, precision := isNumeric(result) isNum, precision := isNumeric(result)
if isNum && precision > 15 { if isNum && precision > 15 {
num, _ := roundPrecision(result) num := roundPrecision(result, -1)
result = strings.ToUpper(num) result = strings.ToUpper(num)
} }
return return
@ -6689,6 +6683,157 @@ func (fn *formulaFuncs) YEAR(argsList *list.List) formulaArg {
return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Year())) return newNumberFormulaArg(float64(timeFromExcelTime(num.Number, false).Year()))
} }
// yearFracBasisCond is an implementation of the yearFracBasis1.
func yearFracBasisCond(sy, sm, sd, ey, em, ed int) bool {
return (isLeapYear(sy) && (sm < 2 || (sm == 2 && sd <= 29))) || (isLeapYear(ey) && (em > 2 || (em == 2 && ed == 29)))
}
// yearFracBasis0 function returns the fraction of a year that between two
// supplied dates in US (NASD) 30/360 type of day.
func yearFracBasis0(startDate, endDate float64) (dayDiff, daysInYear float64) {
startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
sy, smM, sd := startTime.Date()
ey, emM, ed := endTime.Date()
sm, em := int(smM), int(emM)
if sd == 31 {
sd--
}
if sd == 30 && ed == 31 {
ed--
} else if leap := isLeapYear(sy); sm == 2 && ((leap && sd == 29) || (!leap && sd == 28)) {
sd = 30
if leap := isLeapYear(ey); em == 2 && ((leap && ed == 29) || (!leap && ed == 28)) {
ed = 30
}
}
dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))
daysInYear = 360
return
}
// yearFracBasis1 function returns the fraction of a year that between two
// supplied dates in actual type of day.
func yearFracBasis1(startDate, endDate float64) (dayDiff, daysInYear float64) {
startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
sy, smM, sd := startTime.Date()
ey, emM, ed := endTime.Date()
sm, em := int(smM), int(emM)
dayDiff = endDate - startDate
isYearDifferent := sy != ey
if isYearDifferent && (ey != sy+1 || sm < em || (sm == em && sd < ed)) {
dayCount := 0
for y := sy; y <= ey; y++ {
dayCount += getYearDays(y, 1)
}
daysInYear = float64(dayCount) / float64(ey-sy+1)
} else {
if !isYearDifferent && isLeapYear(sy) {
daysInYear = 366
} else {
if isYearDifferent && yearFracBasisCond(sy, sm, sd, ey, em, ed) {
daysInYear = 366
} else {
daysInYear = 365
}
}
}
return
}
// yearFracBasis4 function returns the fraction of a year that between two
// supplied dates in European 30/360 type of day.
func yearFracBasis4(startDate, endDate float64) (dayDiff, daysInYear float64) {
startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
sy, smM, sd := startTime.Date()
ey, emM, ed := endTime.Date()
sm, em := int(smM), int(emM)
if sd == 31 {
sd--
}
if ed == 31 {
ed--
}
dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))
daysInYear = 360
return
}
// yearFrac is an implementation of the formula function YEARFRAC.
func yearFrac(startDate, endDate float64, basis int) formulaArg {
startTime, endTime := timeFromExcelTime(startDate, false), timeFromExcelTime(endDate, false)
if startTime == endTime {
return newNumberFormulaArg(0)
}
var dayDiff, daysInYear float64
switch basis {
case 0:
dayDiff, daysInYear = yearFracBasis0(startDate, endDate)
case 1:
dayDiff, daysInYear = yearFracBasis1(startDate, endDate)
case 2:
dayDiff = endDate - startDate
daysInYear = 360
case 3:
dayDiff = endDate - startDate
daysInYear = 365
case 4:
dayDiff, daysInYear = yearFracBasis4(startDate, endDate)
default:
return newErrorFormulaArg(formulaErrorNUM, "invalid basis")
}
return newNumberFormulaArg(dayDiff / daysInYear)
}
// getYearDays return days of the year with specifying the type of day count
// basis to be used.
func getYearDays(year, basis int) int {
switch basis {
case 1:
if isLeapYear(year) {
return 366
}
return 365
case 3:
return 365
default:
return 360
}
}
// YEARFRAC function returns the fraction of a year that is represented by the
// number of whole days between two supplied dates. The syntax of the
// function is:
//
// YEARFRAC(start_date,end_date,[basis])
//
func (fn *formulaFuncs) YEARFRAC(argsList *list.List) formulaArg {
if argsList.Len() != 2 && argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, "YEARFRAC requires 3 or 4 arguments")
}
var basisArg formulaArg
startArg, endArg := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber()
args := list.New().Init()
if startArg.Type != ArgNumber {
args.PushBack(argsList.Front().Value.(formulaArg))
if startArg = fn.DATEVALUE(args); startArg.Type != ArgNumber {
return startArg
}
}
if endArg.Type != ArgNumber {
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
if endArg = fn.DATEVALUE(args); endArg.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
}
if argsList.Len() == 3 {
if basisArg = argsList.Back().Value.(formulaArg).ToNumber(); basisArg.Type != ArgNumber {
return basisArg
}
}
return yearFrac(startArg.Number, endArg.Number, int(basisArg.Number))
}
// NOW function returns the current date and time. The function receives no // NOW function returns the current date and time. The function receives no
// arguments and therefore. The syntax of the function is: // arguments and therefore. The syntax of the function is:
// //

View File

@ -57,12 +57,12 @@ func TestCalcCellValue(t *testing.T) {
// BESSELJ // BESSELJ
"=BESSELJ(1.9,2)": "0.329925727692387", "=BESSELJ(1.9,2)": "0.329925727692387",
// BESSELK // BESSELK
"=BESSELK(0.05,0)": "3.114234034289662", "=BESSELK(0.05,0)": "3.11423403428966",
"=BESSELK(0.05,1)": "19.90967432724863", "=BESSELK(0.05,1)": "19.90967432724863",
"=BESSELK(0.05,2)": "799.501207124235", "=BESSELK(0.05,2)": "799.501207124235",
"=BESSELK(3,2)": "0.061510458561912", "=BESSELK(3,2)": "0.0615104585619118",
// BESSELY // BESSELY
"=BESSELY(0.05,0)": "-1.979311006841528", "=BESSELY(0.05,0)": "-1.97931100684153",
"=BESSELY(0.05,1)": "-12.789855163794034", "=BESSELY(0.05,1)": "-12.789855163794034",
"=BESSELY(0.05,2)": "-509.61489554491976", "=BESSELY(0.05,2)": "-509.61489554491976",
"=BESSELY(9,2)": "-0.229082087487741", "=BESSELY(9,2)": "-0.229082087487741",
@ -169,7 +169,7 @@ func TestCalcCellValue(t *testing.T) {
"=IMCOS(0.5)": "0.877582561890373", "=IMCOS(0.5)": "0.877582561890373",
"=IMCOS(\"3+0.5i\")": "-1.1163412445261518-0.0735369737112366i", "=IMCOS(\"3+0.5i\")": "-1.1163412445261518-0.0735369737112366i",
// IMCOSH // IMCOSH
"=IMCOSH(0.5)": "1.127625965206381", "=IMCOSH(0.5)": "1.12762596520638",
"=IMCOSH(\"3+0.5i\")": "8.835204606500994+4.802825082743033i", "=IMCOSH(\"3+0.5i\")": "8.835204606500994+4.802825082743033i",
"=IMCOSH(\"2-i\")": "2.0327230070196656-3.0518977991518i", "=IMCOSH(\"2-i\")": "2.0327230070196656-3.0518977991518i",
"=IMCOSH(COMPLEX(1,-1))": "0.8337300251311491-0.9888977057628651i", "=IMCOSH(COMPLEX(1,-1))": "0.8337300251311491-0.9888977057628651i",
@ -188,7 +188,7 @@ func TestCalcCellValue(t *testing.T) {
"=IMDIV(COMPLEX(5,2),COMPLEX(0,1))": "2-5i", "=IMDIV(COMPLEX(5,2),COMPLEX(0,1))": "2-5i",
// IMEXP // IMEXP
"=IMEXP(0)": "1", "=IMEXP(0)": "1",
"=IMEXP(0.5)": "1.648721270700128", "=IMEXP(0.5)": "1.64872127070013",
"=IMEXP(\"1-2i\")": "-1.1312043837568135-2.4717266720048183i", "=IMEXP(\"1-2i\")": "-1.1312043837568135-2.4717266720048183i",
"=IMEXP(COMPLEX(1,-1))": "1.4686939399158851-2.2873552871788423i", "=IMEXP(COMPLEX(1,-1))": "1.4686939399158851-2.2873552871788423i",
// IMLN // IMLN
@ -243,7 +243,7 @@ func TestCalcCellValue(t *testing.T) {
"=IMSUM(COMPLEX(5,2),COMPLEX(0,1))": "5+3i", "=IMSUM(COMPLEX(5,2),COMPLEX(0,1))": "5+3i",
// IMTAN // IMTAN
"=IMTAN(-0)": "0", "=IMTAN(-0)": "0",
"=IMTAN(0.5)": "0.546302489843791", "=IMTAN(0.5)": "0.54630248984379",
"=IMTAN(\"3+0.5i\")": "-0.11162105077158344+0.46946999342588536i", "=IMTAN(\"3+0.5i\")": "-0.11162105077158344+0.46946999342588536i",
"=IMTAN(\"2-i\")": "-0.24345820118572523-1.16673625724092i", "=IMTAN(\"2-i\")": "-0.24345820118572523-1.16673625724092i",
"=IMTAN(COMPLEX(1,-1))": "0.2717525853195117-1.0839233273386948i", "=IMTAN(COMPLEX(1,-1))": "0.2717525853195117-1.0839233273386948i",
@ -275,21 +275,21 @@ func TestCalcCellValue(t *testing.T) {
"=ABS(ABS(-1))": "1", "=ABS(ABS(-1))": "1",
// ACOS // ACOS
"=ACOS(-1)": "3.141592653589793", "=ACOS(-1)": "3.141592653589793",
"=ACOS(0)": "1.570796326794897", "=ACOS(0)": "1.5707963267949",
"=ACOS(ABS(0))": "1.570796326794897", "=ACOS(ABS(0))": "1.5707963267949",
// ACOSH // ACOSH
"=ACOSH(1)": "0", "=ACOSH(1)": "0",
"=ACOSH(2.5)": "1.566799236972411", "=ACOSH(2.5)": "1.566799236972411",
"=ACOSH(5)": "2.292431669561178", "=ACOSH(5)": "2.29243166956118",
"=ACOSH(ACOSH(5))": "1.471383321536679", "=ACOSH(ACOSH(5))": "1.47138332153668",
// ACOT // ACOT
"=_xlfn.ACOT(1)": "0.785398163397448", "=_xlfn.ACOT(1)": "0.785398163397448",
"=_xlfn.ACOT(-2)": "2.677945044588987", "=_xlfn.ACOT(-2)": "2.677945044588987",
"=_xlfn.ACOT(0)": "1.570796326794897", "=_xlfn.ACOT(0)": "1.5707963267949",
"=_xlfn.ACOT(_xlfn.ACOT(0))": "0.566911504941009", "=_xlfn.ACOT(_xlfn.ACOT(0))": "0.566911504941009",
// ACOTH // ACOTH
"=_xlfn.ACOTH(-5)": "-0.202732554054082", "=_xlfn.ACOTH(-5)": "-0.202732554054082",
"=_xlfn.ACOTH(1.1)": "1.522261218861711", "=_xlfn.ACOTH(1.1)": "1.52226121886171",
"=_xlfn.ACOTH(2)": "0.549306144334055", "=_xlfn.ACOTH(2)": "0.549306144334055",
"=_xlfn.ACOTH(ABS(-2))": "0.549306144334055", "=_xlfn.ACOTH(ABS(-2))": "0.549306144334055",
// ARABIC // ARABIC
@ -299,12 +299,12 @@ func TestCalcCellValue(t *testing.T) {
"=_xlfn.ARABIC(\"\")": "0", "=_xlfn.ARABIC(\"\")": "0",
"=_xlfn.ARABIC(\" ll lc \")": "-50", "=_xlfn.ARABIC(\" ll lc \")": "-50",
// ASIN // ASIN
"=ASIN(-1)": "-1.570796326794897", "=ASIN(-1)": "-1.5707963267949",
"=ASIN(0)": "0", "=ASIN(0)": "0",
"=ASIN(ASIN(0))": "0", "=ASIN(ASIN(0))": "0",
// ASINH // ASINH
"=ASINH(0)": "0", "=ASINH(0)": "0",
"=ASINH(-0.5)": "-0.481211825059604", "=ASINH(-0.5)": "-0.481211825059603",
"=ASINH(2)": "1.44363547517881", "=ASINH(2)": "1.44363547517881",
"=ASINH(ASINH(0))": "0", "=ASINH(ASINH(0))": "0",
// ATAN // ATAN
@ -383,22 +383,22 @@ func TestCalcCellValue(t *testing.T) {
"=COS(COS(0))": "0.54030230586814", "=COS(COS(0))": "0.54030230586814",
// COSH // COSH
"=COSH(0)": "1", "=COSH(0)": "1",
"=COSH(0.5)": "1.127625965206381", "=COSH(0.5)": "1.12762596520638",
"=COSH(-2)": "3.762195691083632", "=COSH(-2)": "3.76219569108363",
"=COSH(COSH(0))": "1.543080634815244", "=COSH(COSH(0))": "1.54308063481524",
// _xlfn.COT // _xlfn.COT
"=_xlfn.COT(0.785398163397448)": "1.000000000000001", "=_xlfn.COT(0.785398163397448)": "1",
"=_xlfn.COT(_xlfn.COT(0.45))": "-0.545473116787229", "=_xlfn.COT(_xlfn.COT(0.45))": "-0.545473116787229",
// _xlfn.COTH // _xlfn.COTH
"=_xlfn.COTH(-3.14159265358979)": "-1.003741873197322", "=_xlfn.COTH(-3.14159265358979)": "-1.00374187319732",
"=_xlfn.COTH(_xlfn.COTH(1))": "1.156014018113954", "=_xlfn.COTH(_xlfn.COTH(1))": "1.15601401811395",
// _xlfn.CSC // _xlfn.CSC
"=_xlfn.CSC(-6)": "3.578899547254406", "=_xlfn.CSC(-6)": "3.57889954725441",
"=_xlfn.CSC(1.5707963267949)": "1", "=_xlfn.CSC(1.5707963267949)": "1",
"=_xlfn.CSC(_xlfn.CSC(1))": "1.077851840310882", "=_xlfn.CSC(_xlfn.CSC(1))": "1.07785184031088",
// _xlfn.CSCH // _xlfn.CSCH
"=_xlfn.CSCH(-3.14159265358979)": "-0.086589537530047", "=_xlfn.CSCH(-3.14159265358979)": "-0.0865895375300472",
"=_xlfn.CSCH(_xlfn.CSCH(1))": "1.044510103955183", "=_xlfn.CSCH(_xlfn.CSCH(1))": "1.04451010395518",
// _xlfn.DECIMAL // _xlfn.DECIMAL
`=_xlfn.DECIMAL("1100",2)`: "12", `=_xlfn.DECIMAL("1100",2)`: "12",
`=_xlfn.DECIMAL("186A0",16)`: "100000", `=_xlfn.DECIMAL("186A0",16)`: "100000",
@ -419,9 +419,9 @@ func TestCalcCellValue(t *testing.T) {
"=EVEN((0))": "0", "=EVEN((0))": "0",
// EXP // EXP
"=EXP(100)": "2.6881171418161356E+43", "=EXP(100)": "2.6881171418161356E+43",
"=EXP(0.1)": "1.105170918075648", "=EXP(0.1)": "1.10517091807565",
"=EXP(0)": "1", "=EXP(0)": "1",
"=EXP(-5)": "0.006737946999085", "=EXP(-5)": "0.00673794699908547",
"=EXP(EXP(0))": "2.718281828459045", "=EXP(EXP(0))": "2.718281828459045",
// FACT // FACT
"=FACT(3)": "6", "=FACT(3)": "6",
@ -502,18 +502,18 @@ func TestCalcCellValue(t *testing.T) {
"=LN(1)": "0", "=LN(1)": "0",
"=LN(100)": "4.605170185988092", "=LN(100)": "4.605170185988092",
"=LN(0.5)": "-0.693147180559945", "=LN(0.5)": "-0.693147180559945",
"=LN(LN(100))": "1.527179625807901", "=LN(LN(100))": "1.5271796258079",
// LOG // LOG
"=LOG(64,2)": "6", "=LOG(64,2)": "6",
"=LOG(100)": "2", "=LOG(100)": "2",
"=LOG(4,0.5)": "-2", "=LOG(4,0.5)": "-2",
"=LOG(500)": "2.698970004336019", "=LOG(500)": "2.69897000433602",
"=LOG(LOG(100))": "0.301029995663981", "=LOG(LOG(100))": "0.301029995663981",
// LOG10 // LOG10
"=LOG10(100)": "2", "=LOG10(100)": "2",
"=LOG10(1000)": "3", "=LOG10(1000)": "3",
"=LOG10(0.001)": "-3", "=LOG10(0.001)": "-3",
"=LOG10(25)": "1.397940008672038", "=LOG10(25)": "1.39794000867204",
"=LOG10(LOG10(100))": "0.301029995663981", "=LOG10(LOG10(100))": "0.301029995663981",
// IMLOG2 // IMLOG2
"=IMLOG2(\"5+2i\")": "2.4289904975637864+0.5489546632866347i", "=IMLOG2(\"5+2i\")": "2.4289904975637864+0.5489546632866347i",
@ -626,9 +626,9 @@ func TestCalcCellValue(t *testing.T) {
"=_xlfn.SEC(0)": "1", "=_xlfn.SEC(0)": "1",
"=_xlfn.SEC(_xlfn.SEC(0))": "0.54030230586814", "=_xlfn.SEC(_xlfn.SEC(0))": "0.54030230586814",
// SECH // SECH
"=_xlfn.SECH(-3.14159265358979)": "0.086266738334055", "=_xlfn.SECH(-3.14159265358979)": "0.0862667383340547",
"=_xlfn.SECH(0)": "1", "=_xlfn.SECH(0)": "1",
"=_xlfn.SECH(_xlfn.SECH(0))": "0.648054273663886", "=_xlfn.SECH(_xlfn.SECH(0))": "0.648054273663885",
// SIGN // SIGN
"=SIGN(9.5)": "1", "=SIGN(9.5)": "1",
"=SIGN(-9.5)": "-1", "=SIGN(-9.5)": "-1",
@ -665,10 +665,10 @@ func TestCalcCellValue(t *testing.T) {
"=STDEVA(MUNIT(2))": "0.577350269189626", "=STDEVA(MUNIT(2))": "0.577350269189626",
"=STDEVA(0,INT(0))": "0", "=STDEVA(0,INT(0))": "0",
// POISSON.DIST // POISSON.DIST
"=POISSON.DIST(20,25,FALSE)": "0.051917468608491", "=POISSON.DIST(20,25,FALSE)": "0.0519174686084913",
"=POISSON.DIST(35,40,TRUE)": "0.242414197690103", "=POISSON.DIST(35,40,TRUE)": "0.242414197690103",
// POISSON // POISSON
"=POISSON(20,25,FALSE)": "0.051917468608491", "=POISSON(20,25,FALSE)": "0.0519174686084913",
"=POISSON(35,40,TRUE)": "0.242414197690103", "=POISSON(35,40,TRUE)": "0.242414197690103",
// SUM // SUM
"=SUM(1,2)": "3", "=SUM(1,2)": "3",
@ -760,15 +760,15 @@ func TestCalcCellValue(t *testing.T) {
"=GAMMA(1.5)": "0.886226925452758", "=GAMMA(1.5)": "0.886226925452758",
"=GAMMA(5.5)": "52.34277778455352", "=GAMMA(5.5)": "52.34277778455352",
// GAMMALN // GAMMALN
"=GAMMALN(4.5)": "2.453736570842443", "=GAMMALN(4.5)": "2.45373657084244",
"=GAMMALN(INT(1))": "0", "=GAMMALN(INT(1))": "0",
// HARMEAN // HARMEAN
"=HARMEAN(2.5,3,0.5,1,3)": "1.229508196721312", "=HARMEAN(2.5,3,0.5,1,3)": "1.22950819672131",
"=HARMEAN(\"2.5\",3,0.5,1,INT(3),\"\")": "1.229508196721312", "=HARMEAN(\"2.5\",3,0.5,1,INT(3),\"\")": "1.22950819672131",
// KURT // KURT
"=KURT(F1:F9)": "-1.033503502551368", "=KURT(F1:F9)": "-1.03350350255137",
"=KURT(F1,F2:F9)": "-1.033503502551368", "=KURT(F1,F2:F9)": "-1.03350350255137",
"=KURT(INT(1),MUNIT(2))": "-3.333333333333336", "=KURT(INT(1),MUNIT(2))": "-3.33333333333334",
// NORM.DIST // NORM.DIST
"=NORM.DIST(0.8,1,0.3,TRUE)": "0.252492537546923", "=NORM.DIST(0.8,1,0.3,TRUE)": "0.252492537546923",
"=NORM.DIST(50,40,20,FALSE)": "0.017603266338215", "=NORM.DIST(50,40,20,FALSE)": "0.017603266338215",
@ -993,6 +993,29 @@ func TestCalcCellValue(t *testing.T) {
"=YEAR(42171)": "2015", "=YEAR(42171)": "2015",
"=YEAR(\"29-May-2015\")": "2015", "=YEAR(\"29-May-2015\")": "2015",
"=YEAR(\"05/03/1984\")": "1984", "=YEAR(\"05/03/1984\")": "1984",
// YEARFRAC
"=YEARFRAC(42005,42005)": "0",
"=YEARFRAC(42005,42094)": "0.25",
"=YEARFRAC(42005,42094,0)": "0.25",
"=YEARFRAC(42005,42094,1)": "0.243835616438356",
"=YEARFRAC(42005,42094,2)": "0.247222222222222",
"=YEARFRAC(42005,42094,3)": "0.243835616438356",
"=YEARFRAC(42005,42094,4)": "0.247222222222222",
"=YEARFRAC(\"01/01/2015\",\"03/31/2015\")": "0.25",
"=YEARFRAC(\"01/01/2015\",\"03/31/2015\",0)": "0.25",
"=YEARFRAC(\"01/01/2015\",\"03/31/2015\",1)": "0.243835616438356",
"=YEARFRAC(\"01/01/2015\",\"03/31/2015\",2)": "0.247222222222222",
"=YEARFRAC(\"01/01/2015\",\"03/31/2015\",3)": "0.243835616438356",
"=YEARFRAC(\"01/01/2015\",\"03/31/2015\",4)": "0.247222222222222",
"=YEARFRAC(\"01/01/2015\",42094)": "0.25",
"=YEARFRAC(42005,\"03/31/2015\",0)": "0.25",
"=YEARFRAC(\"01/31/2015\",\"03/31/2015\")": "0.166666666666667",
"=YEARFRAC(\"01/30/2015\",\"03/31/2015\")": "0.166666666666667",
"=YEARFRAC(\"02/29/2000\", \"02/29/2008\")": "8",
"=YEARFRAC(\"02/29/2000\", \"02/29/2008\",1)": "7.998175182481752",
"=YEARFRAC(\"02/29/2000\", \"01/29/2001\",1)": "0.915300546448087",
"=YEARFRAC(\"02/29/2000\", \"03/29/2000\",1)": "0.0792349726775956",
"=YEARFRAC(\"01/31/2000\", \"03/29/2000\",4)": "0.163888888888889",
// Text Functions // Text Functions
// CHAR // CHAR
"=CHAR(65)": "A", "=CHAR(65)": "A",
@ -1246,7 +1269,7 @@ func TestCalcCellValue(t *testing.T) {
"=ISPMT(0.05/12,2,60,50000)": "-201.38888888888886", "=ISPMT(0.05/12,2,60,50000)": "-201.38888888888886",
"=ISPMT(0.05/12,2,1,50000)": "208.33333333333334", "=ISPMT(0.05/12,2,1,50000)": "208.33333333333334",
// NOMINAL // NOMINAL
"=NOMINAL(0.025,12)": "0.024718035238113", "=NOMINAL(0.025,12)": "0.0247180352381129",
// NPER // NPER
"=NPER(0.04,-6000,50000)": "10.338035071507665", "=NPER(0.04,-6000,50000)": "10.338035071507665",
"=NPER(0,-6000,50000)": "8.333333333333334", "=NPER(0,-6000,50000)": "8.333333333333334",
@ -2062,6 +2085,12 @@ func TestCalcCellValue(t *testing.T) {
"=YEAR(-1)": "YEAR only accepts positive argument", "=YEAR(-1)": "YEAR only accepts positive argument",
"=YEAR(\"text\")": "#VALUE!", "=YEAR(\"text\")": "#VALUE!",
"=YEAR(\"January 25, 100\")": "#VALUE!", "=YEAR(\"January 25, 100\")": "#VALUE!",
// YEARFRAC
"=YEARFRAC()": "YEARFRAC requires 3 or 4 arguments",
"=YEARFRAC(42005,42094,5)": "invalid basis",
"=YEARFRAC(\"\",42094,5)": "#VALUE!",
"=YEARFRAC(42005,\"\",5)": "#VALUE!",
"=YEARFRAC(42005,42094,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
// NOW // NOW
"=NOW(A1)": "NOW accepts no arguments", "=NOW(A1)": "NOW accepts no arguments",
// TODAY // TODAY
@ -2412,8 +2441,8 @@ func TestCalcCellValue(t *testing.T) {
"=(-2-SUM(-4+A2))*5": "0", "=(-2-SUM(-4+A2))*5": "0",
"=SUM(Sheet1!A1:Sheet1!A1:A2,A2)": "5", "=SUM(Sheet1!A1:Sheet1!A1:A2,A2)": "5",
"=SUM(A1,A2,A3)*SUM(2,3)": "30", "=SUM(A1,A2,A3)*SUM(2,3)": "30",
"=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.333333333333334", "=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.33333333333333",
"=A1/A2/SUM(A1:A2:B1)": "0.041666666666667", "=A1/A2/SUM(A1:A2:B1)": "0.0416666666666667",
"=A1/A2/SUM(A1:A2:B1)*A3": "0.125", "=A1/A2/SUM(A1:A2:B1)*A3": "0.125",
"=SUM(B1:D1)": "4", "=SUM(B1:D1)": "4",
"=SUM(\"X\")": "0", "=SUM(\"X\")": "0",
@ -2760,7 +2789,7 @@ func TestCalcMIRR(t *testing.T) {
cellData := [][]interface{}{{-100}, {18}, {22.5}, {28}, {35.5}, {45}} cellData := [][]interface{}{{-100}, {18}, {22.5}, {28}, {35.5}, {45}}
f := prepareCalcData(cellData) f := prepareCalcData(cellData)
formulaList := map[string]string{ formulaList := map[string]string{
"=MIRR(A1:A5,0.055,0.05)": "0.025376365108071", "=MIRR(A1:A5,0.055,0.05)": "0.0253763651080707",
"=MIRR(A1:A6,0.055,0.05)": "0.1000268752662", "=MIRR(A1:A6,0.055,0.05)": "0.1000268752662",
} }
for formula, expected := range formulaList { for formula, expected := range formulaList {
@ -2848,3 +2877,9 @@ func TestStrToDate(t *testing.T) {
_, _, _, _, err := strToDate("") _, _, _, _, err := strToDate("")
assert.Equal(t, formulaErrorVALUE, err.Error) assert.Equal(t, formulaErrorVALUE, err.Error)
} }
func TestGetYearDays(t *testing.T) {
for _, data := range [][]int{{2021, 0, 360}, {2000, 1, 366}, {2021, 1, 365}, {2000, 3, 365}} {
assert.Equal(t, data[2], getYearDays(data[0], data[1]))
}
}

22
cell.go
View File

@ -70,15 +70,6 @@ func (f *File) GetCellValue(sheet, axis string, opts ...Options) (string, error)
// GetCellType provides a function to get the cell's data type by given // GetCellType provides a function to get the cell's data type by given
// worksheet name and axis in spreadsheet file. // worksheet name and axis in spreadsheet file.
func (f *File) GetCellType(sheet, axis string) (CellType, error) { func (f *File) GetCellType(sheet, axis string) (CellType, error) {
cellTypes := map[string]CellType{
"b": CellTypeBool,
"d": CellTypeDate,
"n": CellTypeNumber,
"e": CellTypeError,
"s": CellTypeString,
"str": CellTypeString,
"inlineStr": CellTypeString,
}
var ( var (
err error err error
cellTypeStr string cellTypeStr string
@ -1046,9 +1037,16 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
precise := v precise := v
isNum, precision := isNumeric(v) isNum, precision := isNumeric(v)
if isNum && precision > 10 { if isNum && precision > 10 {
precise, _ = roundPrecision(v) precise = roundPrecision(v, -1)
} }
if s == 0 || raw { if raw {
return v
}
if !isNum {
v = roundPrecision(v, 15)
precise = v
}
if s == 0 {
return precise return precise
} }
styleSheet := f.stylesReader() styleSheet := f.stylesReader()
@ -1063,7 +1061,7 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
ok := builtInNumFmtFunc[numFmtID] ok := builtInNumFmtFunc[numFmtID]
if ok != nil { if ok != nil {
return ok(v, builtInNumFmt[numFmtID]) return ok(precise, builtInNumFmt[numFmtID])
} }
if styleSheet == nil || styleSheet.NumFmts == nil { if styleSheet == nil || styleSheet.NumFmts == nil {
return precise return precise

View File

@ -245,10 +245,66 @@ func TestGetCellValue(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
f.Sheet.Delete("xl/worksheets/sheet1.xml") f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="1"><c r="A1"><v>2422.3000000000002</v></c><c r="B1"><v>2422.3000000000002</v></c><c r="C1"><v>12.4</v></c><c r="D1"><v>964</v></c><c r="E1"><v>1101.5999999999999</v></c><c r="F1"><v>275.39999999999998</v></c><c r="G1"><v>68.900000000000006</v></c><c r="H1"><v>44385.208333333336</v></c><c r="I1"><v>5.0999999999999996</v></c><c r="J1"><v>5.1100000000000003</v></c><c r="K1"><v>5.0999999999999996</v></c><c r="L1"><v>5.1109999999999998</v></c><c r="M1"><v>5.1111000000000004</v></c><c r="N1"><v>2422.012345678</v></c><c r="O1"><v>2422.0123456789</v></c><c r="P1"><v>12.012345678901</v></c><c r="Q1"><v>964</v></c><c r="R1"><v>1101.5999999999999</v></c><c r="S1"><v>275.39999999999998</v></c><c r="T1"><v>68.900000000000006</v></c></row>`))) f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="1">
<c r="A1"><v>2422.3000000000002</v></c>
<c r="B1"><v>2422.3000000000002</v></c>
<c r="C1"><v>12.4</v></c>
<c r="D1"><v>964</v></c>
<c r="E1"><v>1101.5999999999999</v></c>
<c r="F1"><v>275.39999999999998</v></c>
<c r="G1"><v>68.900000000000006</v></c>
<c r="H1"><v>44385.208333333336</v></c>
<c r="I1"><v>5.0999999999999996</v></c>
<c r="J1"><v>5.1100000000000003</v></c>
<c r="K1"><v>5.0999999999999996</v></c>
<c r="L1"><v>5.1109999999999998</v></c>
<c r="M1"><v>5.1111000000000004</v></c>
<c r="N1"><v>2422.012345678</v></c>
<c r="O1"><v>2422.0123456789</v></c>
<c r="P1"><v>12.012345678901</v></c>
<c r="Q1"><v>964</v></c>
<c r="R1"><v>1101.5999999999999</v></c>
<c r="S1"><v>275.39999999999998</v></c>
<c r="T1"><v>68.900000000000006</v></c>
<c r="U1"><v>8.8880000000000001E-2</v></c>
<c r="V1"><v>4.0000000000000003E-5</v></c>
<c r="W1"><v>2422.3000000000002</v></c>
<c r="X1"><v>1101.5999999999999</v></c>
<c r="Y1"><v>275.39999999999998</v></c>
<c r="Z1"><v>68.900000000000006</v></c>
<c r="AA1"><v>1.1000000000000001</v></c>
</row>`)))
f.checked = nil f.checked = nil
rows, err = f.GetRows("Sheet1") rows, err = f.GetRows("Sheet1")
assert.Equal(t, [][]string{{"2422.3", "2422.3", "12.4", "964", "1101.6", "275.4", "68.9", "44385.20833333333", "5.1", "5.11", "5.1", "5.111", "5.1111", "2422.012345678", "2422.0123456789", "12.012345678901", "964", "1101.6", "275.4", "68.9"}}, rows) assert.Equal(t, [][]string{{
"2422.3",
"2422.3",
"12.4",
"964",
"1101.6",
"275.4",
"68.9",
"44385.2083333333",
"5.1",
"5.11",
"5.1",
"5.111",
"5.1111",
"2422.012345678",
"2422.0123456789",
"12.012345678901",
"964",
"1101.6",
"275.4",
"68.9",
"0.08888",
"0.00004",
"2422.3",
"1101.6",
"275.4",
"68.9",
"1.1",
}}, rows)
assert.NoError(t, err) assert.NoError(t, err)
} }

20
rows.go
View File

@ -18,6 +18,7 @@ import (
"io" "io"
"log" "log"
"math" "math"
"math/big"
"os" "os"
"strconv" "strconv"
@ -421,14 +422,19 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
} }
} }
// roundPrecision round precision for numeric. // roundPrecision provides a function to format floating-point number text
func roundPrecision(value string) (result string, err error) { // with precision, if the given text couldn't be parsed to float, this will
var num float64 // return the original string.
if num, err = strconv.ParseFloat(value, 64); err != nil { func roundPrecision(text string, prec int) string {
return decimal := big.Float{}
if _, ok := decimal.SetString(text); ok {
flt, _ := decimal.Float64()
if prec == -1 {
return decimal.Text('G', 15)
} }
result = fmt.Sprintf("%g", math.Round(num*numericPrecision)/numericPrecision) return strconv.FormatFloat(flt, 'f', -1, 64)
return }
return text
} }
// SetRowVisible provides a function to set visible of a single row by given // SetRowVisible provides a function to set visible of a single row by given

View File

@ -884,11 +884,6 @@ func TestGetValueFromNumber(t *testing.T) {
} }
} }
func TestRoundPrecision(t *testing.T) {
_, err := roundPrecision("")
assert.EqualError(t, err, "strconv.ParseFloat: parsing \"\": invalid syntax")
}
func TestErrSheetNotExistError(t *testing.T) { func TestErrSheetNotExistError(t *testing.T) {
err := ErrSheetNotExist{SheetName: "Sheet1"} err := ErrSheetNotExist{SheetName: "Sheet1"}
assert.EqualValues(t, err.Error(), "sheet Sheet1 is not exist") assert.EqualValues(t, err.Error(), "sheet Sheet1 is not exist")