Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
此号慢热型
excelize
提交
68dd5b34
excelize
项目概览
此号慢热型
/
excelize
与 Fork 源项目一致
Fork自
xuri / excelize
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
excelize
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
已验证
提交
68dd5b34
编写于
4月 13, 2021
作者:
xurime
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
#65 fn: DB, DDB, DOLLARDE, DOLLARFR, EFFECT, ISPMT and NOMINAL
上级
a13b21fe
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
320 addition
and
0 deletion
+320
-0
calc.go
calc.go
+246
-0
calc_test.go
calc_test.go
+74
-0
未找到文件。
calc.go
浏览文件 @
68dd5b34
...
...
@@ -265,11 +265,16 @@ var tokenPriority = map[string]int{
// CUMPRINC
// DATE
// DATEDIF
// DB
// DDB
// DEC2BIN
// DEC2HEX
// DEC2OCT
// DECIMAL
// DEGREES
// DOLLARDE
// DOLLARFR
// EFFECT
// ENCODEURL
// EVEN
// EXACT
...
...
@@ -332,6 +337,7 @@ var tokenPriority = map[string]int{
// ISODD
// ISTEXT
// ISO.CEILING
// ISPMT
// KURT
// LARGE
// LCM
...
...
@@ -357,6 +363,7 @@ var tokenPriority = map[string]int{
// MUNIT
// N
// NA
// NOMINAL
// NORM.DIST
// NORMDIST
// NORM.INV
...
...
@@ -7237,6 +7244,185 @@ func (fn *formulaFuncs) cumip(name string, argsList *list.List) formulaArg {
return
newNumberFormulaArg
(
num
)
}
// DB function calculates the depreciation of an asset, using the Fixed
// Declining Balance Method, for each period of the asset's lifetime. The
// syntax of the function is:
//
// DB(cost,salvage,life,period,[month])
//
func
(
fn
*
formulaFuncs
)
DB
(
argsList
*
list
.
List
)
formulaArg
{
if
argsList
.
Len
()
<
4
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"DB requires at least 4 arguments"
)
}
if
argsList
.
Len
()
>
5
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"DB allows at most 5 arguments"
)
}
cost
:=
argsList
.
Front
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
cost
.
Type
!=
ArgNumber
{
return
cost
}
salvage
:=
argsList
.
Front
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
salvage
.
Type
!=
ArgNumber
{
return
salvage
}
life
:=
argsList
.
Front
()
.
Next
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
life
.
Type
!=
ArgNumber
{
return
life
}
period
:=
argsList
.
Front
()
.
Next
()
.
Next
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
period
.
Type
!=
ArgNumber
{
return
period
}
month
:=
newNumberFormulaArg
(
12
)
if
argsList
.
Len
()
==
5
{
if
month
=
argsList
.
Back
()
.
Value
.
(
formulaArg
)
.
ToNumber
();
month
.
Type
!=
ArgNumber
{
return
month
}
}
if
cost
.
Number
==
0
{
return
newNumberFormulaArg
(
0
)
}
if
(
cost
.
Number
<=
0
)
||
((
salvage
.
Number
/
cost
.
Number
)
<
0
)
||
(
life
.
Number
<=
0
)
||
(
period
.
Number
<
1
)
||
(
month
.
Number
<
1
)
{
return
newErrorFormulaArg
(
formulaErrorNA
,
formulaErrorNA
)
}
dr
:=
1
-
math
.
Pow
(
salvage
.
Number
/
cost
.
Number
,
1
/
life
.
Number
)
dr
=
math
.
Round
(
dr
*
1000
)
/
1000
pd
,
depreciation
:=
0.0
,
0.0
for
per
:=
1
;
per
<=
int
(
period
.
Number
);
per
++
{
if
per
==
1
{
depreciation
=
cost
.
Number
*
dr
*
month
.
Number
/
12
}
else
if
per
==
int
(
life
.
Number
+
1
)
{
depreciation
=
(
cost
.
Number
-
pd
)
*
dr
*
(
12
-
month
.
Number
)
/
12
}
else
{
depreciation
=
(
cost
.
Number
-
pd
)
*
dr
}
pd
+=
depreciation
}
return
newNumberFormulaArg
(
depreciation
)
}
// DDB function calculates the depreciation of an asset, using the Double
// Declining Balance Method, or another specified depreciation rate. The
// syntax of the function is:
//
// DDB(cost,salvage,life,period,[factor])
//
func
(
fn
*
formulaFuncs
)
DDB
(
argsList
*
list
.
List
)
formulaArg
{
if
argsList
.
Len
()
<
4
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"DDB requires at least 4 arguments"
)
}
if
argsList
.
Len
()
>
5
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"DDB allows at most 5 arguments"
)
}
cost
:=
argsList
.
Front
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
cost
.
Type
!=
ArgNumber
{
return
cost
}
salvage
:=
argsList
.
Front
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
salvage
.
Type
!=
ArgNumber
{
return
salvage
}
life
:=
argsList
.
Front
()
.
Next
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
life
.
Type
!=
ArgNumber
{
return
life
}
period
:=
argsList
.
Front
()
.
Next
()
.
Next
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
period
.
Type
!=
ArgNumber
{
return
period
}
factor
:=
newNumberFormulaArg
(
2
)
if
argsList
.
Len
()
==
5
{
if
factor
=
argsList
.
Back
()
.
Value
.
(
formulaArg
)
.
ToNumber
();
factor
.
Type
!=
ArgNumber
{
return
factor
}
}
if
cost
.
Number
==
0
{
return
newNumberFormulaArg
(
0
)
}
if
(
cost
.
Number
<=
0
)
||
((
salvage
.
Number
/
cost
.
Number
)
<
0
)
||
(
life
.
Number
<=
0
)
||
(
period
.
Number
<
1
)
||
(
factor
.
Number
<=
0.0
)
||
(
period
.
Number
>
life
.
Number
)
{
return
newErrorFormulaArg
(
formulaErrorNA
,
formulaErrorNA
)
}
pd
,
depreciation
:=
0.0
,
0.0
for
per
:=
1
;
per
<=
int
(
period
.
Number
);
per
++
{
depreciation
=
math
.
Min
((
cost
.
Number
-
pd
)
*
(
factor
.
Number
/
life
.
Number
),
(
cost
.
Number
-
salvage
.
Number
-
pd
))
pd
+=
depreciation
}
return
newNumberFormulaArg
(
depreciation
)
}
// DOLLARDE function converts a dollar value in fractional notation, into a
// dollar value expressed as a decimal. The syntax of the function is:
//
// DOLLARDE(fractional_dollar,fraction)
//
func
(
fn
*
formulaFuncs
)
DOLLARDE
(
argsList
*
list
.
List
)
formulaArg
{
return
fn
.
dollar
(
"DOLLARDE"
,
argsList
)
}
// DOLLARFR function converts a dollar value in decimal notation, into a
// dollar value that is expressed in fractional notation. The syntax of the
// function is:
//
// DOLLARFR(decimal_dollar,fraction)
//
func
(
fn
*
formulaFuncs
)
DOLLARFR
(
argsList
*
list
.
List
)
formulaArg
{
return
fn
.
dollar
(
"DOLLARFR"
,
argsList
)
}
// dollar is an implementation of the formula function DOLLARDE and DOLLARFR.
func
(
fn
*
formulaFuncs
)
dollar
(
name
string
,
argsList
*
list
.
List
)
formulaArg
{
if
argsList
.
Len
()
!=
2
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
fmt
.
Sprintf
(
"%s requires 2 arguments"
,
name
))
}
dollar
:=
argsList
.
Front
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
dollar
.
Type
!=
ArgNumber
{
return
dollar
}
frac
:=
argsList
.
Back
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
frac
.
Type
!=
ArgNumber
{
return
frac
}
if
frac
.
Number
<
0
{
return
newErrorFormulaArg
(
formulaErrorNUM
,
formulaErrorNUM
)
}
if
frac
.
Number
==
0
{
return
newErrorFormulaArg
(
formulaErrorDIV
,
formulaErrorDIV
)
}
cents
:=
math
.
Mod
(
dollar
.
Number
,
1
)
if
name
==
"DOLLARDE"
{
cents
/=
frac
.
Number
cents
*=
math
.
Pow
(
10
,
math
.
Ceil
(
math
.
Log10
(
frac
.
Number
)))
}
else
{
cents
*=
frac
.
Number
cents
*=
math
.
Pow
(
10
,
-
math
.
Ceil
(
math
.
Log10
(
frac
.
Number
)))
}
return
newNumberFormulaArg
(
math
.
Floor
(
dollar
.
Number
)
+
cents
)
}
// EFFECT function returns the effective annual interest rate for a given
// nominal interest rate and number of compounding periods per year. The
// syntax of the function is:
//
// EFFECT(nominal_rate,npery)
//
func
(
fn
*
formulaFuncs
)
EFFECT
(
argsList
*
list
.
List
)
formulaArg
{
if
argsList
.
Len
()
!=
2
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"EFFECT requires 2 arguments"
)
}
rate
:=
argsList
.
Front
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
rate
.
Type
!=
ArgNumber
{
return
rate
}
npery
:=
argsList
.
Back
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
npery
.
Type
!=
ArgNumber
{
return
npery
}
if
rate
.
Number
<=
0
||
npery
.
Number
<
1
{
return
newErrorFormulaArg
(
formulaErrorNUM
,
formulaErrorNUM
)
}
return
newNumberFormulaArg
(
math
.
Pow
((
1
+
rate
.
Number
/
npery
.
Number
),
npery
.
Number
)
-
1
)
}
// IPMT function calculates the interest payment, during a specific period of a
// loan or investment that is paid in constant periodic payments, with a
// constant interest rate. The syntax of the function is:
...
...
@@ -7310,6 +7496,66 @@ func (fn *formulaFuncs) ipmt(name string, argsList *list.List) formulaArg {
return
newNumberFormulaArg
(
principal
)
}
// ISPMT function calculates the interest paid during a specific period of a
// loan or investment. The syntax of the function is:
//
// ISPMT(rate,per,nper,pv)
//
func
(
fn
*
formulaFuncs
)
ISPMT
(
argsList
*
list
.
List
)
formulaArg
{
if
argsList
.
Len
()
!=
4
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"ISPMT requires 4 arguments"
)
}
rate
:=
argsList
.
Front
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
rate
.
Type
!=
ArgNumber
{
return
rate
}
per
:=
argsList
.
Front
()
.
Next
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
per
.
Type
!=
ArgNumber
{
return
per
}
nper
:=
argsList
.
Back
()
.
Prev
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
nper
.
Type
!=
ArgNumber
{
return
nper
}
pv
:=
argsList
.
Back
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
pv
.
Type
!=
ArgNumber
{
return
pv
}
pr
,
payment
,
num
:=
pv
.
Number
,
pv
.
Number
/
nper
.
Number
,
0.0
for
i
:=
0
;
i
<=
int
(
per
.
Number
);
i
++
{
num
=
rate
.
Number
*
pr
*
-
1
pr
-=
payment
if
i
==
int
(
nper
.
Number
)
{
num
=
0
}
}
return
newNumberFormulaArg
(
num
)
}
// NOMINAL function returns the nominal interest rate for a given effective
// interest rate and number of compounding periods per year. The syntax of
// the function is:
//
// NOMINAL(effect_rate,npery)
//
func
(
fn
*
formulaFuncs
)
NOMINAL
(
argsList
*
list
.
List
)
formulaArg
{
if
argsList
.
Len
()
!=
2
{
return
newErrorFormulaArg
(
formulaErrorVALUE
,
"NOMINAL requires 2 arguments"
)
}
rate
:=
argsList
.
Front
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
rate
.
Type
!=
ArgNumber
{
return
rate
}
npery
:=
argsList
.
Back
()
.
Value
.
(
formulaArg
)
.
ToNumber
()
if
npery
.
Type
!=
ArgNumber
{
return
npery
}
if
rate
.
Number
<=
0
||
npery
.
Number
<
1
{
return
newErrorFormulaArg
(
formulaErrorNUM
,
formulaErrorNUM
)
}
return
newNumberFormulaArg
(
npery
.
Number
*
(
math
.
Pow
(
rate
.
Number
+
1
,
1
/
npery
.
Number
)
-
1
))
}
// PMT function calculates the constant periodic payment required to pay off
// (or partially pay off) a loan or investment, with a constant interest
// rate, over a specified period. The syntax of the function is:
...
...
calc_test.go
浏览文件 @
68dd5b34
...
...
@@ -1139,9 +1139,35 @@ func TestCalcCellValue(t *testing.T) {
// CUMPRINC
"=CUMPRINC(0.05/12,60,50000,1,12,0)"
:
"-9027.762649079885"
,
"=CUMPRINC(0.05/12,60,50000,13,24,0)"
:
"-9489.640119832635"
,
// DB
"=DB(0,1000,5,1)"
:
"0"
,
"=DB(10000,1000,5,1)"
:
"3690"
,
"=DB(10000,1000,5,2)"
:
"2328.39"
,
"=DB(10000,1000,5,1,6)"
:
"1845"
,
"=DB(10000,1000,5,6,6)"
:
"238.52712458788187"
,
// DDB
"=DDB(0,1000,5,1)"
:
"0"
,
"=DDB(10000,1000,5,1)"
:
"4000"
,
"=DDB(10000,1000,5,2)"
:
"2400"
,
"=DDB(10000,1000,5,3)"
:
"1440"
,
"=DDB(10000,1000,5,4)"
:
"864"
,
"=DDB(10000,1000,5,5)"
:
"296"
,
// DOLLARDE
"=DOLLARDE(1.01,16)"
:
"1.0625"
,
// DOLLARFR
"=DOLLARFR(1.0625,16)"
:
"1.01"
,
// EFFECT
"=EFFECT(0.1,4)"
:
"0.103812890625"
,
"=EFFECT(0.025,2)"
:
"0.02515625"
,
// IPMT
"=IPMT(0.05/12,2,60,50000)"
:
"-205.26988187971995"
,
"=IPMT(0.035/4,2,8,0,5000,1)"
:
"5.257455237829077"
,
// ISPMT
"=ISPMT(0.05/12,1,60,50000)"
:
"-204.8611111111111"
,
"=ISPMT(0.05/12,2,60,50000)"
:
"-201.38888888888886"
,
"=ISPMT(0.05/12,2,1,50000)"
:
"208.33333333333334"
,
// NOMINAL
"=NOMINAL(0.025,12)"
:
"0.024718035238113"
,
// PMT
"=PMT(0,8,0,5000,1)"
:
"-625"
,
"=PMT(0.035/4,8,0,5000,1)"
:
"-600.8520271804658"
,
...
...
@@ -2058,6 +2084,42 @@ func TestCalcCellValue(t *testing.T) {
"=CUMPRINC(0,0,0,
\"\"
,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=CUMPRINC(0,0,0,0,
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=CUMPRINC(0,0,0,0,0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
// DB
"=DB()"
:
"DB requires at least 4 arguments"
,
"=DB(0,0,0,0,0,0)"
:
"DB allows at most 5 arguments"
,
"=DB(-1,0,0,0)"
:
"#N/A"
,
"=DB(
\"\"
,0,0,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DB(0,
\"\"
,0,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DB(0,0,
\"\"
,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DB(0,0,0,
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DB(0,0,0,0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
// DDB
"=DDB()"
:
"DDB requires at least 4 arguments"
,
"=DDB(0,0,0,0,0,0)"
:
"DDB allows at most 5 arguments"
,
"=DDB(-1,0,0,0)"
:
"#N/A"
,
"=DDB(
\"\"
,0,0,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DDB(0,
\"\"
,0,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DDB(0,0,
\"\"
,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DDB(0,0,0,
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DDB(0,0,0,0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
// DOLLARDE
"=DOLLARDE()"
:
"DOLLARDE requires 2 arguments"
,
"=DOLLARDE(
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DOLLARDE(0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DOLLARDE(0,-1)"
:
"#NUM!"
,
"=DOLLARDE(0,0)"
:
"#DIV/0!"
,
// DOLLARFR
"=DOLLARFR()"
:
"DOLLARFR requires 2 arguments"
,
"=DOLLARFR(
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DOLLARFR(0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=DOLLARFR(0,-1)"
:
"#NUM!"
,
"=DOLLARFR(0,0)"
:
"#DIV/0!"
,
// EFFECT
"=EFFECT()"
:
"EFFECT requires 2 arguments"
,
"=EFFECT(
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=EFFECT(0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=EFFECT(0,0)"
:
"#NUM!"
,
"=EFFECT(1,0)"
:
"#NUM!"
,
// IPMT
"=IPMT()"
:
"IPMT requires at least 4 arguments"
,
"=IPMT(0,0,0,0,0,0,0)"
:
"IPMT allows at most 6 arguments"
,
...
...
@@ -2070,6 +2132,18 @@ func TestCalcCellValue(t *testing.T) {
"=IPMT(0,0,0,
\"\"
,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=IPMT(0,0,0,0,
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=IPMT(0,0,0,0,0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
// ISPMT
"=ISPMT()"
:
"ISPMT requires 4 arguments"
,
"=ISPMT(
\"\"
,0,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=ISPMT(0,
\"\"
,0,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=ISPMT(0,0,
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=ISPMT(0,0,0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
// NOMINAL
"=NOMINAL()"
:
"NOMINAL requires 2 arguments"
,
"=NOMINAL(
\"\"
,0)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=NOMINAL(0,
\"\"
)"
:
"strconv.ParseFloat: parsing
\"\"
: invalid syntax"
,
"=NOMINAL(0,0)"
:
"#NUM!"
,
"=NOMINAL(1,0)"
:
"#NUM!"
,
// PMT
"=PMT()"
:
"PMT requires at least 3 arguments"
,
"=PMT(0,0,0,0,0,0)"
:
"PMT allows at most 5 arguments"
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录