From 599a8cb0bceb6cb14d3018360bb4c5140753c2b3 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 19 Nov 2020 21:38:35 +0800 Subject: [PATCH] Fixed #727, rounding numeric with precision for formula calculation --- README.md | 2 +- README_zh.md | 2 +- calc.go | 15 ++++++++++ calc_test.go | 84 ++++++++++++++++++++++++++-------------------------- rows.go | 21 ++++++------- 5 files changed, 70 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 891641b..6afcc7e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Go Report Card go.dev Licenses - Donate + Donate

# Excelize diff --git a/README_zh.md b/README_zh.md index deba22a..f54cf01 100644 --- a/README_zh.md +++ b/README_zh.md @@ -6,7 +6,7 @@ Go Report Card go.dev Licenses - Donate + Donate

# Excelize diff --git a/calc.go b/calc.go index bc3b6e9..577cfaa 100644 --- a/calc.go +++ b/calc.go @@ -42,6 +42,14 @@ const ( formulaErrorGETTINGDATA = "#GETTING_DATA" ) +// 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. +const numericPrecision = 1000000000000000 + // cellRef defines the structure of a cell reference. type cellRef struct { Col int @@ -141,6 +149,13 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) { return } result = token.TValue + if len(result) > 16 { + num, e := roundPrecision(result) + if e != nil { + return result, err + } + result = strings.ToUpper(num) + } return } diff --git a/calc_test.go b/calc_test.go index 9bf6e08..6998b77 100644 --- a/calc_test.go +++ b/calc_test.go @@ -54,42 +54,42 @@ func TestCalcCellValue(t *testing.T) { "=ABS(2-4.5)": "2.5", // ACOS "=ACOS(-1)": "3.141592653589793", - "=ACOS(0)": "1.5707963267948966", + "=ACOS(0)": "1.570796326794897", // ACOSH "=ACOSH(1)": "0", "=ACOSH(2.5)": "1.566799236972411", - "=ACOSH(5)": "2.2924316695611777", + "=ACOSH(5)": "2.292431669561178", // ACOT - "=_xlfn.ACOT(1)": "0.7853981633974483", + "=_xlfn.ACOT(1)": "0.785398163397448", "=_xlfn.ACOT(-2)": "2.677945044588987", - "=_xlfn.ACOT(0)": "1.5707963267948966", + "=_xlfn.ACOT(0)": "1.570796326794897", // ACOTH - "=_xlfn.ACOTH(-5)": "-0.2027325540540822", - "=_xlfn.ACOTH(1.1)": "1.5222612188617113", - "=_xlfn.ACOTH(2)": "0.5493061443340548", + "=_xlfn.ACOTH(-5)": "-0.202732554054082", + "=_xlfn.ACOTH(1.1)": "1.522261218861711", + "=_xlfn.ACOTH(2)": "0.549306144334055", // ARABIC `=_xlfn.ARABIC("IV")`: "4", `=_xlfn.ARABIC("-IV")`: "-4", `=_xlfn.ARABIC("MCXX")`: "1120", `=_xlfn.ARABIC("")`: "0", // ASIN - "=ASIN(-1)": "-1.5707963267948966", + "=ASIN(-1)": "-1.570796326794897", "=ASIN(0)": "0", // ASINH "=ASINH(0)": "0", - "=ASINH(-0.5)": "-0.48121182505960347", - "=ASINH(2)": "1.4436354751788103", + "=ASINH(-0.5)": "-0.481211825059604", + "=ASINH(2)": "1.44363547517881", // ATAN - "=ATAN(-1)": "-0.7853981633974483", + "=ATAN(-1)": "-0.785398163397448", "=ATAN(0)": "0", - "=ATAN(1)": "0.7853981633974483", + "=ATAN(1)": "0.785398163397448", // ATANH - "=ATANH(-0.8)": "-1.0986122886681098", + "=ATANH(-0.8)": "-1.09861228866811", "=ATANH(0)": "0", - "=ATANH(0.5)": "0.5493061443340548", + "=ATANH(0.5)": "0.549306144334055", // ATAN2 - "=ATAN2(1,1)": "0.7853981633974483", - "=ATAN2(1,-1)": "-0.7853981633974483", + "=ATAN2(1,1)": "0.785398163397448", + "=ATAN2(1,-1)": "-0.785398163397448", "=ATAN2(4,0)": "0", // BASE "=BASE(12,2)": "1100", @@ -145,17 +145,17 @@ func TestCalcCellValue(t *testing.T) { "=COS(0)": "1", // COSH "=COSH(0)": "1", - "=COSH(0.5)": "1.1276259652063807", - "=COSH(-2)": "3.7621956910836314", + "=COSH(0.5)": "1.127625965206381", + "=COSH(-2)": "3.762195691083632", // _xlfn.COT - "=_xlfn.COT(0.785398163397448)": "0.9999999999999992", + "=_xlfn.COT(0.785398163397448)": "0.999999999999999", // _xlfn.COTH - "=_xlfn.COTH(-3.14159265358979)": "-0.9962720762207499", + "=_xlfn.COTH(-3.14159265358979)": "-0.99627207622075", // _xlfn.CSC - "=_xlfn.CSC(-6)": "3.5788995472544056", + "=_xlfn.CSC(-6)": "3.578899547254406", "=_xlfn.CSC(1.5707963267949)": "1", // _xlfn.CSCH - "=_xlfn.CSCH(-3.14159265358979)": "-0.08658953753004724", + "=_xlfn.CSCH(-3.14159265358979)": "-0.086589537530047", // _xlfn.DECIMAL `=_xlfn.DECIMAL("1100",2)`: "12", `=_xlfn.DECIMAL("186A0",16)`: "100000", @@ -174,9 +174,9 @@ func TestCalcCellValue(t *testing.T) { "=EVEN(-4)": "-4", // EXP "=EXP(100)": "2.6881171418161356E+43", - "=EXP(0.1)": "1.1051709180756477", + "=EXP(0.1)": "1.105170918075648", "=EXP(0)": "1", - "=EXP(-5)": "0.006737946999085467", + "=EXP(-5)": "0.006737946999085", // FACT "=FACT(3)": "6", "=FACT(6)": "720", @@ -247,23 +247,23 @@ func TestCalcCellValue(t *testing.T) { // LN "=LN(1)": "0", "=LN(100)": "4.605170185988092", - "=LN(0.5)": "-0.6931471805599453", + "=LN(0.5)": "-0.693147180559945", // LOG "=LOG(64,2)": "6", "=LOG(100)": "2", "=LOG(4,0.5)": "-2", - "=LOG(500)": "2.6989700043360183", + "=LOG(500)": "2.698970004336019", // LOG10 "=LOG10(100)": "2", "=LOG10(1000)": "3", "=LOG10(0.001)": "-3", - "=LOG10(25)": "1.3979400086720375", + "=LOG10(25)": "1.397940008672038", // MOD "=MOD(6,4)": "2", "=MOD(6,3)": "0", "=MOD(6,2.5)": "1", - "=MOD(6,1.333)": "0.6680000000000001", - "=MOD(-10.23,1)": "0.7699999999999996", + "=MOD(6,1.333)": "0.668", + "=MOD(-10.23,1)": "0.77", // MROUND "=MROUND(333.7,0.5)": "333.5", "=MROUND(333.8,1)": "334", @@ -298,7 +298,7 @@ func TestCalcCellValue(t *testing.T) { "=QUOTIENT(4.5,3.1)": "1", "=QUOTIENT(-10,3)": "-3", // RADIANS - "=RADIANS(50)": "0.8726646259971648", + "=RADIANS(50)": "0.872664625997165", "=RADIANS(-180)": "-3.141592653589793", "=RADIANS(180)": "3.141592653589793", "=RADIANS(360)": "6.283185307179586", @@ -323,13 +323,13 @@ func TestCalcCellValue(t *testing.T) { "=ROUND(991,-1)": "990", // ROUNDDOWN "=ROUNDDOWN(99.999,1)": "99.9", - "=ROUNDDOWN(99.999,2)": "99.99000000000002", + "=ROUNDDOWN(99.999,2)": "99.99000000000001", "=ROUNDDOWN(99.999,0)": "99", "=ROUNDDOWN(99.999,-1)": "90", - "=ROUNDDOWN(-99.999,2)": "-99.99000000000002", + "=ROUNDDOWN(-99.999,2)": "-99.99000000000001", "=ROUNDDOWN(-99.999,-1)": "-90", // ROUNDUP - "=ROUNDUP(11.111,1)": "11.200000000000001", + "=ROUNDUP(11.111,1)": "11.200000000000003", "=ROUNDUP(11.111,2)": "11.120000000000003", "=ROUNDUP(11.111,0)": "12", "=ROUNDUP(11.111,-1)": "20", @@ -339,7 +339,7 @@ func TestCalcCellValue(t *testing.T) { "=_xlfn.SEC(-3.14159265358979)": "-1", "=_xlfn.SEC(0)": "1", // SECH - "=_xlfn.SECH(-3.14159265358979)": "0.0862667383340547", + "=_xlfn.SECH(-3.14159265358979)": "0.086266738334055", "=_xlfn.SECH(0)": "1", // SIGN "=SIGN(9.5)": "1", @@ -348,17 +348,17 @@ func TestCalcCellValue(t *testing.T) { "=SIGN(0.00000001)": "1", "=SIGN(6-7)": "-1", // SIN - "=SIN(0.785398163)": "0.7071067809055092", + "=SIN(0.785398163)": "0.707106780905509", // SINH "=SINH(0)": "0", - "=SINH(0.5)": "0.5210953054937474", + "=SINH(0.5)": "0.521095305493747", "=SINH(-2)": "-3.626860407847019", // SQRT "=SQRT(4)": "2", `=SQRT("")`: "0", // SQRTPI "=SQRTPI(5)": "3.963327297606011", - "=SQRTPI(0.2)": "0.7926654595212022", + "=SQRTPI(0.2)": "0.792665459521202", "=SQRTPI(100)": "17.72453850905516", "=SQRTPI(0)": "0", // SUM @@ -399,8 +399,8 @@ func TestCalcCellValue(t *testing.T) { "=TAN(0)": "0", // TANH "=TANH(0)": "0", - "=TANH(0.5)": "0.46211715726000974", - "=TANH(-2)": "-0.9640275800758169", + "=TANH(0.5)": "0.46211715726001", + "=TANH(-2)": "-0.964027580075817", // TRUNC "=TRUNC(99.999,1)": "99.9", "=TRUNC(99.999,2)": "99.99", @@ -794,14 +794,14 @@ func TestCalcCellValue(t *testing.T) { // PRODUCT "=PRODUCT(Sheet1!A1:Sheet1!A1:A2,A2)": "4", // SUM - "=A1/A3": "0.3333333333333333", + "=A1/A3": "0.333333333333333", "=SUM(A1:A2)": "3", "=SUM(Sheet1!A1,A2)": "3", "=(-2-SUM(-4+A2))*5": "0", "=SUM(Sheet1!A1:Sheet1!A1:A2,A2)": "5", "=SUM(A1,A2,A3)*SUM(2,3)": "30", - "=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.3333333333333335", - "=A1/A2/SUM(A1:A2:B1)": "0.041666666666666664", + "=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.333333333333334", + "=A1/A2/SUM(A1:A2:B1)": "0.041666666666667", "=A1/A2/SUM(A1:A2:B1)*A3": "0.125", } for formula, expected := range referenceCalc { diff --git a/rows.go b/rows.go index 4f93ed1..591d7e9 100644 --- a/rows.go +++ b/rows.go @@ -345,20 +345,11 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) { } return f.formattedValue(c.S, c.V), nil default: - // correct numeric values as legacy Excel app - // 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. - const precision = 1000000000000000 if len(c.V) > 16 { - num, err := strconv.ParseFloat(c.V, 64) + val, err := roundPrecision(c.V) if err != nil { return "", err } - - num = math.Round(num*precision) / precision - val := fmt.Sprintf("%g", num) if val != c.V { return f.formattedValue(c.S, val), nil } @@ -367,6 +358,16 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) { } } +// roundPrecision round precision for numeric. +func roundPrecision(value string) (result string, err error) { + var num float64 + if num, err = strconv.ParseFloat(value, 64); err != nil { + return + } + result = fmt.Sprintf("%g", math.Round(num*numericPrecision)/numericPrecision) + return +} + // SetRowVisible provides a function to set visible of a single row by given // worksheet name and Excel row number. For example, hide row 2 in Sheet1: //