Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,48 @@ func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSiz

// setCellFloat prepares cell type and string type cell value by a given float
// value.
func (c *xlsxC) setCellFloat(value float64, precision, bitSize int) {
func (c *xlsxC) setCellFloat(value float64, prec, bitSize int) {
if math.IsNaN(value) || math.IsInf(value, 0) {
c.setInlineStr(fmt.Sprint(value))
return
}
c.T, c.V = "", strconv.FormatFloat(value, 'f', precision, bitSize)
c.T, c.V = "", formatCellFloat(value, prec, bitSize)
c.IS = nil
}

// formatCellFloat formats a floating point value with Excel's 15 significant
// digit limit when automatic precision is requested.
func formatCellFloat(value float64, prec, bitSize int) string {
str := strconv.FormatFloat(value, 'f', prec, bitSize)
if prec != -1 || math.Abs(value) == math.MaxFloat64 {
return str
}
const maxPrec = 15
decimal := strings.IndexByte(str, '.')
significant := 0
for idx := range str {
if str[idx] < '0' || str[idx] > '9' || significant == 0 && str[idx] == '0' {
continue
}
significant++
if significant <= maxPrec {
continue
}
if decimal >= 0 && idx > decimal {
return strings.TrimSuffix(str[:idx], ".")
}
if decimal < 0 {
decimal = len(str)
}
truncated := []byte(str[:decimal])
for ; idx < len(truncated); idx++ {
truncated[idx] = '0'
}
return string(truncated)
}
return str
}

// SetCellStr provides a function to set string type value of a cell. Total
// number of characters that a cell can contain 32767 characters.
func (f *File) SetCellStr(sheet, cell, value string) error {
Expand Down Expand Up @@ -641,17 +674,33 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
}
return f.formattedValue(c, raw, CellTypeInlineString)
default:
if isNum, precision, decimal := isNumeric(c.V); isNum && !raw {
if precision > 15 {
c.V = strconv.FormatFloat(decimal, 'G', 15, 64)
} else {
c.V = strconv.FormatFloat(decimal, 'f', -1, 64)
}
}
c.getCellDefault(raw)
return f.formattedValue(c, raw, CellTypeNumber)
}
}

// getCellDefault provides a function to get default value from cell by given
// raw option.
func (c *xlsxC) getCellDefault(raw bool) {
if isNum, prec, decimal := isNumeric(c.V); isNum && !raw {
if prec > 15 && !isNumWithinPrecision(c.V) {
c.V = strconv.FormatFloat(decimal, 'G', 15, 64)
} else {
c.V = strconv.FormatFloat(decimal, 'f', -1, 64)
}
}
}

// isNumWithinPrecision checks for integers zero-filled after Excel's 15
// significant digit limit.
func isNumWithinPrecision(str string) bool {
if strings.ContainsAny(str, ".Ee") {
return false
}
str = strings.TrimLeft(str, "+-0")
return len(strings.TrimRight(str, "0")) <= 15
}

// SetCellDefault provides a function to set string type value of a cell as
// default format without escaping the cell.
func (f *File) SetCellDefault(sheet, cell, value string) error {
Expand Down
21 changes: 21 additions & 0 deletions cell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,27 @@ func TestSetCellValue(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expected, val)
}
t.Run("for_decimal_precision_overflow", func(t *testing.T) {
f := NewFile()
defer assert.NoError(t, f.Close())
for val, expected := range map[float64]string{
0.123456789012345678: "0.123456789012345",
-1234567890.12345678: "-1234567890.12345",
1234567890.1234: "1234567890.1234",
1234567890.12345678: "1234567890.12345",
123456789012345.67: "123456789012345",
1234567890123456: "1234567890123450",
9007199254740993.01: "9007199254740990",
} {
assert.NoError(t, f.SetCellValue("Sheet1", "A1", val))
got, err := f.GetCellValue("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, expected, got)
got, err = f.GetCellValue("Sheet1", "A1", Options{RawCellValue: true})
assert.NoError(t, err)
assert.Equal(t, expected, got)
}
})
}

func TestSetCellBool(t *testing.T) {
Expand Down