From 4dd34477f7e0d726c13660b03a9a1da421f05cd3 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 18 Jul 2024 21:05:36 +0800 Subject: [PATCH] This closes #1955, refs #119, support to set cell value with an IEEE 754 "not-a-number" value or infinity --- cell.go | 17 ++++++++++++----- cell_test.go | 13 +++++++++++++ stream.go | 4 ++-- stream_test.go | 5 ++++- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/cell.go b/cell.go index 959b408..95caea3 100644 --- a/cell.go +++ b/cell.go @@ -15,6 +15,7 @@ import ( "bytes" "encoding/xml" "fmt" + "math" "os" "reflect" "strconv" @@ -385,6 +386,9 @@ func setCellBool(value bool) (t string, v string) { // var x float32 = 1.325 // f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32) func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSize int) error { + if math.IsNaN(value) || math.IsInf(value, 0) { + return f.SetCellStr(sheet, cell, fmt.Sprint(value)) + } f.mu.Lock() ws, err := f.workSheetReader(sheet) if err != nil { @@ -399,16 +403,19 @@ func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSiz return err } c.S = ws.prepareCellStyle(col, row, c.S) - c.T, c.V = setCellFloat(value, precision, bitSize) - c.IS = nil + c.setCellFloat(value, precision, bitSize) return f.removeFormula(c, ws, sheet) } // setCellFloat prepares cell type and string type cell value by a given float // value. -func setCellFloat(value float64, precision, bitSize int) (t string, v string) { - v = strconv.FormatFloat(value, 'f', precision, bitSize) - return +func (c *xlsxC) setCellFloat(value float64, precision, 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.IS = nil } // SetCellStr provides a function to set string type value of a cell. Total diff --git a/cell_test.go b/cell_test.go index be974d5..e64e464 100644 --- a/cell_test.go +++ b/cell_test.go @@ -292,6 +292,19 @@ func TestSetCellValue(t *testing.T) { val, err = f.GetCellValue("Sheet1", "B1") assert.NoError(t, err) assert.Equal(t, "b", val) + + f = NewFile() + // Test set cell value with an IEEE 754 "not-a-number" value or infinity + for num, expected := range map[float64]string{ + math.NaN(): "NaN", + math.Inf(0): "+Inf", + math.Inf(-1): "-Inf", + } { + assert.NoError(t, f.SetCellValue("Sheet1", "A1", num)) + val, err := f.GetCellValue("Sheet1", "A1") + assert.NoError(t, err) + assert.Equal(t, expected, val) + } } func TestSetCellValues(t *testing.T) { diff --git a/stream.go b/stream.go index 189732f..125fb4b 100644 --- a/stream.go +++ b/stream.go @@ -529,9 +529,9 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error { case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: setCellIntFunc(c, val) case float32: - c.T, c.V = setCellFloat(float64(val), -1, 32) + c.setCellFloat(float64(val), -1, 32) case float64: - c.T, c.V = setCellFloat(val, -1, 64) + c.setCellFloat(val, -1, 64) case string: c.setCellValue(val) case []byte: diff --git a/stream_test.go b/stream_test.go index d7c116c..61387fe 100644 --- a/stream_test.go +++ b/stream_test.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "fmt" "io" + "math" "math/rand" "os" "path/filepath" @@ -76,6 +77,8 @@ func TestStreamWriter(t *testing.T) { assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID})) assert.Equal(t, ErrMaxRowHeight, streamWriter.SetRow("A8", nil, RowOpts{Height: MaxRowHeight + 1})) + assert.NoError(t, streamWriter.SetRow("A9", []interface{}{math.NaN(), math.Inf(0), math.Inf(-1)})) + for rowID := 10; rowID <= 51200; rowID++ { row := make([]interface{}, 50) for colID := 0; colID < 50; colID++ { @@ -145,7 +148,7 @@ func TestStreamWriter(t *testing.T) { cells += len(row) } assert.NoError(t, rows.Close()) - assert.Equal(t, 2559559, cells) + assert.Equal(t, 2559562, cells) // Save spreadsheet with password. assert.NoError(t, file.SaveAs(filepath.Join("test", "EncryptionTestStreamWriter.xlsx"), Options{Password: "password"})) assert.NoError(t, file.Close())