Repository: VictoriaMetrics/VictoriaMetrics
Pull request #217 (2019-10-25 07:12:22)

#215: update klauspost/compress lib
#215 

codecov-io: # [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=h1) Report
> Merging [#217](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=desc) into [master](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/commit/3cea200309e6047e8119848dbb60617c49ebd3d2?src=pr&el=desc) will **decrease** coverage by `0.07%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217/graphs/tree.svg?width=650&token=YND8dYVcXn&height=150&src=pr)](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #217      +/-   ##
==========================================
- Coverage   69.21%   69.14%   -0.08%     
==========================================
  Files          95       95              
  Lines       16039    16039              
==========================================
- Hits        11102    11090      -12     
- Misses       3989     3997       +8     
- Partials      948      952       +4
```


| [Impacted Files](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [app/vmselect/promql/aggr\_incremental.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217/diff?src=pr&el=tree#diff-YXBwL3Ztc2VsZWN0L3Byb21xbC9hZ2dyX2luY3JlbWVudGFsLmdv) | `93.38% <0%> (-5.15%)` | :arrow_down: |
| [lib/mergeset/table.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217/diff?src=pr&el=tree#diff-bGliL21lcmdlc2V0L3RhYmxlLmdv) | `69.18% <0%> (-0.3%)` | :arrow_down: |
| [lib/storage/merge.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217/diff?src=pr&el=tree#diff-bGliL3N0b3JhZ2UvbWVyZ2UuZ28=) | `82.64% <0%> (+3.3%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=footer). Last update [3cea200...c7c060e](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/217?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

valyala: It looks like the issue isn't fixed yet in zstd 1.9.0 according to #215 comments. Let's wait until it is confirmed as fixed.
klauspost: @hagen1778 `v1.9.1` released 👍 

Patch:
diff --git a/app/vmselect/prometheus/prometheus.go b/app/vmselect/prometheus/prometheus.go
index 9c39c7064..8264781af 100644
--- a/app/vmselect/prometheus/prometheus.go
+++ b/app/vmselect/prometheus/prometheus.go
@@ -21,6 +21,8 @@ import (
 )
 
 var (
+	latencyOffset = flag.Duration("search.latencyOffset", time.Second*60, "The time when data points become visible in query results after the colection. "+
+		"Too small value can result in incomplete last points for query results")
 	maxQueryDuration = flag.Duration("search.maxQueryDuration", time.Second*30, "The maximum time for search query execution")
 	maxQueryLen      = flag.Int("search.maxQueryLen", 16*1024, "The maximum search query length in bytes")
 	maxLookback      = flag.Duration("search.maxLookback", 0, "Synonim to `-search.lookback-delta` from Prometheus. "+
@@ -30,10 +32,6 @@ var (
 // Default step used if not set.
 const defaultStep = 5 * 60 * 1000
 
-// Latency for data processing pipeline, i.e. the time between data is ignested
-// into the system and the time it becomes visible to search.
-const latencyOffset = 60 * 1000
-
 // FederateHandler implements /federate . See https://prometheus.io/docs/prometheus/latest/federation/
 func FederateHandler(w http.ResponseWriter, r *http.Request) error {
 	startTime := time.Now()
@@ -468,7 +466,8 @@ func QueryHandler(w http.ResponseWriter, r *http.Request) error {
 	if err != nil {
 		return err
 	}
-	step, err := getDuration(r, "step", latencyOffset)
+	queryOffset := getLatencyOffsetMilliseconds()
+	step, err := getDuration(r, "step", queryOffset)
 	if err != nil {
 		return err
 	}
@@ -481,8 +480,8 @@ func QueryHandler(w http.ResponseWriter, r *http.Request) error {
 	if len(query) > *maxQueryLen {
 		return fmt.Errorf(`too long query; got %d bytes; mustn't exceed %d bytes`, len(query), *maxQueryLen)
 	}
-	if ct-start < latencyOffset {
-		start -= latencyOffset
+	if ct-start < queryOffset {
+		start -= queryOffset
 	}
 	if childQuery, windowStr, offsetStr := promql.IsMetricSelectorWithRollup(query); childQuery != "" {
 		var window int64
@@ -587,7 +586,8 @@ func QueryRangeHandler(w http.ResponseWriter, r *http.Request) error {
 	if err != nil {
 		return fmt.Errorf("cannot execute %q: %s", query, err)
 	}
-	if ct-end < latencyOffset {
+	queryOffset := getLatencyOffsetMilliseconds()
+	if ct-end < queryOffset {
 		result = adjustLastPoints(result)
 	}
 
@@ -784,3 +784,11 @@ func getTagFilterssFromMatches(matches []string) ([][]storage.TagFilter, error)
 	}
 	return tagFilterss, nil
 }
+
+func getLatencyOffsetMilliseconds() int64 {
+	d := int64(*latencyOffset / time.Millisecond)
+	if d <= 1000 {
+		d = 1000
+	}
+	return d
+}
diff --git a/go.mod b/go.mod
index 0c418eef8..b4c9bc4c0 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/cespare/xxhash/v2 v2.1.0
 	github.com/golang/snappy v0.0.1
 	github.com/google/go-cmp v0.3.0 // indirect
-	github.com/klauspost/compress v1.8.6
+	github.com/klauspost/compress v1.9.1
 	github.com/valyala/fastjson v1.4.1
 	github.com/valyala/fastrand v1.0.0
 	github.com/valyala/gozstd v1.6.2
diff --git a/go.sum b/go.sum
index 99feb32a3..6e451a393 100644
--- a/go.sum
+++ b/go.sum
@@ -24,6 +24,10 @@ github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
 github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.8.6 h1:970MQcQdxX7hfgc/aqmB4a3grW0ivUVV6i1TLkP8CiE=
 github.com/klauspost/compress v1.8.6/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.0 h1:GhthINjveNZAdFUD8QoQYfjxnOONZgztK/Yr6M23UTY=
+github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.1 h1:TWy0o9J9c6LK9C8t7Msh6IAJNXbsU/nvKLTQUU5HdaY=
+github.com/klauspost/compress v1.9.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
diff --git a/lib/decimal/decimal.go b/lib/decimal/decimal.go
index 60c1d3005..b9b35ace7 100644
--- a/lib/decimal/decimal.go
+++ b/lib/decimal/decimal.go
@@ -299,12 +299,18 @@ func FromFloat(f float64) (v int64, e int16) {
 
 func positiveFloatToDecimal(f float64) (int64, int16) {
 	var scale int16
-	v := int64(f)
-	if f == float64(v) {
+	u := uint64(f)
+	if float64(u) == f {
 		// Fast path for integers.
-		u := uint64(v)
-		if u%10 != 0 {
-			return v, 0
+		for u >= 1<<54 {
+			// Remove trailing garbage bits left after float64->uint64 conversion,
+			// since float64 contains only 53 significant bits.
+			// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+			u /= 10
+			scale++
+		}
+		if u%10 != 0 || u == 0 {
+			return int64(u), scale
 		}
 		// Minimize v by converting trailing zeros to scale.
 		u /= 10
@@ -317,9 +323,15 @@ func positiveFloatToDecimal(f float64) (int64, int16) {
 	}
 
 	// Slow path for floating point numbers.
+	prec := conversionPrecision
 	if f > 1e6 || f < 1e-6 {
 		// Normalize f, so it is in the small range suitable
 		// for the next loop.
+		if f > 1e6 {
+			// Increase conversion precision for big numbers.
+			// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/213
+			prec = 1e15
+		}
 		_, exp := math.Frexp(f)
 		scale = int16(float64(exp) * math.Ln2 / math.Ln10)
 		f *= math.Pow10(-int(scale))
@@ -327,20 +339,20 @@ func positiveFloatToDecimal(f float64) (int64, int16) {
 
 	// Multiply f by 100 until the fractional part becomes
 	// too small comparing to integer part.
-	for f < conversionPrecision {
+	for f < prec {
 		x, frac := math.Modf(f)
-		if frac*conversionPrecision < x {
+		if frac*prec < x {
 			f = x
 			break
 		}
-		if (1-frac)*conversionPrecision < x {
+		if (1-frac)*prec < x {
 			f = x + 1
 			break
 		}
 		f *= 100
 		scale -= 2
 	}
-	u := uint64(f)
+	u = uint64(f)
 	if u%10 != 0 {
 		return int64(u), scale
 	}
diff --git a/vendor/github.com/klauspost/compress/huff0/compress.go b/vendor/github.com/klauspost/compress/huff0/compress.go
index dd4f7fefb..51e00aaeb 100644
--- a/vendor/github.com/klauspost/compress/huff0/compress.go
+++ b/vendor/github.com/klauspost/compress/huff0/compress.go
@@ -54,6 +54,12 @@ func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)
 		canReuse = s.canUseTable(s.prevTable)
 	}
 
+	// We want the output size to be less than this:
+	wantSize := len(in)
+	if s.WantLogLess > 0 {
+		wantSize -= wantSize >> s.WantLogLess
+	}
+
 	// Reset for next run.
 	s.clearCount = true
 	s.maxCount = 0
@@ -77,7 +83,7 @@ func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)
 		s.cTable = s.prevTable
 		s.Out, err = compressor(in)
 		s.cTable = keepTable
-		if err == nil && len(s.Out) < len(in) {
+		if err == nil && len(s.Out) < wantSize {
 			s.OutData = s.Out
 			return s.Out, true, nil
 		}
@@ -100,13 +106,16 @@ func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)
 		hSize := len(s.Out)
 		oldSize := s.prevTable.estimateSize(s.count[:s.symbolLen])
 		newSize := s.cTable.estimateSize(s.count[:s.symbolLen])
-		if oldSize <= hSize+newSize || hSize+12 >= len(in) {
+		if oldSize <= hSize+newSize || hSize+12 >= wantSize {
 			// Retain cTable even if we re-use.
 			keepTable := s.cTable
 			s.cTable = s.prevTable
 			s.Out, err = compressor(in)
 			s.cTable = keepTable
-			if len(s.Out) >= len(in) {
+			if err != nil {
+				return nil, false, err
+			}
+			if len(s.Out) >= wantSize {
 				return nil, false, ErrIncompressible
 			}
 			s.OutData = s.Out
@@ -128,7 +137,7 @@ func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)
 		s.OutTable = nil
 		return nil, false, err
 	}
-	if len(s.Out) >= len(in) {
+	if len(s.Out) >= wantSize {
 		s.OutTable = nil
 		return nil, false, ErrIncompressible
 	}
diff --git a/vendor/github.com/klauspost/compress/huff0/huff0.go b/vendor/github.com/klauspost/compress/huff0/huff0.go
index 6f823f94d..6bc23bbf0 100644
--- a/vendor/github.com/klauspost/compress/huff0/huff0.go
+++ b/vendor/github.com/klauspost/compress/huff0/huff0.go
@@ -89,6 +89,12 @@ type Scratch struct {
 	// Reuse will specify the reuse policy
 	Reuse ReusePolicy
 
+	// WantLogLess allows to specify a log 2 reduction that should at least be achieved,
+	// otherwise the block will be returned as incompressible.
+	// The reduction should then at least be (input size >> WantLogLess)
+	// If WantLogLess == 0 any improvement will do.
+	WantLogLess uint8
+
 	// MaxDecodedSize will set the maximum allowed output size.
 	// This value will automatically be set to BlockSizeMax if not set.
 	// Decoders will return ErrMaxDecodedSizeExceeded is this limit is exceeded.
diff --git a/vendor/github.com/klauspost/compress/zstd/blockenc.go b/vendor/github.com/klauspost/compress/zstd/blockenc.go
index 9d9151a0e..8383279d2 100644
--- a/vendor/github.com/klauspost/compress/zstd/blockenc.go
+++ b/vendor/github.com/klauspost/compress/zstd/blockenc.go
@@ -51,7 +51,7 @@ func (b *blockEnc) init() {
 		b.coders.llEnc = &fseEncoder{}
 		b.coders.llPrev = &fseEncoder{}
 	}
-	b.litEnc = &huff0.Scratch{}
+	b.litEnc = &huff0.Scratch{WantLogLess: 4}
 	b.reset(nil)
 }
 
@@ -391,6 +391,52 @@ func (b *blockEnc) encodeLits() error {
 	return nil
 }
 
+// fuzzFseEncoder can be used to fuzz the FSE encoder.
+func fuzzFseEncoder(data []byte) int {
+	if len(data) > maxSequences || len(data) < 2 {
+		return 0
+	}
+	enc := fseEncoder{}
+	hist := enc.Histogram()[:256]
+	maxSym := uint8(0)
+	for i, v := range data {
+		v = v & 63
+		data[i] = v
+		hist[v]++
+		if v > maxSym {
+			maxSym = v
+		}
+	}
+	if maxSym == 0 {
+		// All 0
+		return 0
+	}
+	maxCount := func(a []uint32) int {
+		var max uint32
+		for _, v := range a {
+			if v > max {
+				max = v
+			}
+		}
+		return int(max)
+	}
+	cnt := maxCount(hist[:maxSym])
+	if cnt == len(data) {
+		// RLE
+		return 0
+	}
+	enc.HistogramFinished(maxSym, cnt)
+	err := enc.normalizeCount(len(data))
+	if err != nil {
+		return 0
+	}
+	_, err = enc.writeCount(nil)
+	if err != nil {
+		panic(err)
+	}
+	return 1
+}
+
 // encode will encode the block and put the output in b.output.
 func (b *blockEnc) encode() error {
 	if len(b.sequences) == 0 {
@@ -415,16 +461,10 @@ func (b *blockEnc) encode() error {
 	if len(b.literals) >= 1024 {
 		// Use 4 Streams.
 		out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc)
-		if len(out) > len(b.literals)-len(b.literals)>>4 {
-			err = huff0.ErrIncompressible
-		}
 	} else if len(b.literals) > 32 {
 		// Use 1 stream
 		single = true
 		out, reUsed, err = huff0.Compress1X(b.literals, b.litEnc)
-		if len(out) > len(b.literals)-len(b.literals)>>4 {
-			err = huff0.ErrIncompressible
-		}
 	} else {
 		err = huff0.ErrIncompressible
 	}
@@ -711,7 +751,7 @@ func (b *blockEnc) encode() error {
 	return nil
 }
 
-var errIncompressible = errors.New("uncompressible")
+var errIncompressible = errors.New("incompressible")
 
 func (b *blockEnc) genCodes() {
 	if len(b.sequences) == 0 {
diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go
index e120625d8..2f41bcd0d 100644
--- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go
+++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go
@@ -235,7 +235,7 @@ encodeLoop:
 				if debug && s-t > e.maxMatchOff {
 					panic("s - t >e.maxMatchOff")
 				}
-				if debug {
+				if debugMatches {
 					println("long match")
 				}
 				break
@@ -259,7 +259,7 @@ encodeLoop:
 					// but the likelihood of both the first 4 bytes and the hash matching should be enough.
 					t = candidateL.offset - e.cur
 					s += checkAt
-					if debug {
+					if debugMatches {
 						println("long match (after short)")
 					}
 					break
@@ -275,7 +275,7 @@ encodeLoop:
 				if debug && t < 0 {
 					panic("t<0")
 				}
-				if debug {
+				if debugMatches {
 					println("short match")
 				}
 				break
diff --git a/vendor/github.com/klauspost/compress/zstd/fse_encoder.go b/vendor/github.com/klauspost/compress/zstd/fse_encoder.go
index dfa6cf7ce..619836f52 100644
--- a/vendor/github.com/klauspost/compress/zstd/fse_encoder.go
+++ b/vendor/github.com/klauspost/compress/zstd/fse_encoder.go
@@ -502,13 +502,22 @@ func (s *fseEncoder) validateNorm() (err error) {
 // writeCount will write the normalized histogram count to header.
 // This is read back by readNCount.
 func (s *fseEncoder) writeCount(out []byte) ([]byte, error) {
+	if s.useRLE {
+		return append(out, s.rleVal), nil
+	}
+	if s.preDefined || s.reUsed {
+		// Never write predefined.
+		return out, nil
+	}
+
 	var (
 		tableLog  = s.actualTableLog
 		tableSize = 1 << tableLog
 		previous0 bool
 		charnum   uint16
 
-		maxHeaderSize = ((int(s.symbolLen) * int(tableLog)) >> 3) + 3
+		// maximum header size plus 2 extra bytes for final output if bitCount == 0.
+		maxHeaderSize = ((int(s.symbolLen) * int(tableLog)) >> 3) + 3 + 2
 
 		// Write Table Size
 		bitStream = uint32(tableLog - minEncTablelog)
@@ -516,15 +525,12 @@ func (s *fseEncoder) writeCount(out []byte) ([]byte, error) {
 		remaining = int16(tableSize + 1) /* +1 for extra accuracy */
 		threshold = int16(tableSize)
 		nbBits    = uint(tableLog + 1)
+		outP      = len(out)
 	)
-	if s.useRLE {
-		return append(out, s.rleVal), nil
+	if cap(out) < outP+maxHeaderSize {
+		out = append(out, make([]byte, maxHeaderSize*3)...)
+		out = out[:len(out)-maxHeaderSize*3]
 	}
-	if s.preDefined || s.reUsed {
-		// Never write predefined.
-		return out, nil
-	}
-	outP := len(out)
 	out = out[:outP+maxHeaderSize]
 
 	// stops at 1
@@ -594,11 +600,14 @@ func (s *fseEncoder) writeCount(out []byte) ([]byte, error) {
 		}
 	}
 
+	if outP+2 > len(out) {
+		return nil, fmt.Errorf("internal error: %d > %d, maxheader: %d, sl: %d, tl: %d, normcount: %v", outP+2, len(out), maxHeaderSize, s.symbolLen, int(tableLog), s.norm[:s.symbolLen])
+	}
 	out[outP] = byte(bitStream)
 	out[outP+1] = byte(bitStream >> 8)
 	outP += int((bitCount + 7) / 8)
 
-	if uint16(charnum) > s.symbolLen {
+	if charnum > s.symbolLen {
 		return nil, errors.New("internal error: charnum > s.symbolLen")
 	}
 	return out[:outP], nil
diff --git a/vendor/github.com/klauspost/compress/zstd/zstd.go b/vendor/github.com/klauspost/compress/zstd/zstd.go
index b975954c1..57a8a2f5b 100644
--- a/vendor/github.com/klauspost/compress/zstd/zstd.go
+++ b/vendor/github.com/klauspost/compress/zstd/zstd.go
@@ -11,6 +11,7 @@ import (
 
 const debug = false
 const debugSequences = false
+const debugMatches = false
 
 // force encoder to use predefined tables.
 const forcePreDef = false
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3ebffec05..7187c499c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -6,17 +6,17 @@ github.com/VictoriaMetrics/metrics
 github.com/cespare/xxhash/v2
 # github.com/golang/snappy v0.0.1
 github.com/golang/snappy
-# github.com/klauspost/compress v1.8.6
-github.com/klauspost/compress/fse
+# github.com/klauspost/compress v1.9.1
+github.com/klauspost/compress/zstd
 github.com/klauspost/compress/huff0
 github.com/klauspost/compress/snappy
-github.com/klauspost/compress/zstd
 github.com/klauspost/compress/zstd/internal/xxhash
+github.com/klauspost/compress/fse
 # github.com/valyala/bytebufferpool v1.0.0
 github.com/valyala/bytebufferpool
 # github.com/valyala/fastjson v1.4.1
-github.com/valyala/fastjson
 github.com/valyala/fastjson/fastfloat
+github.com/valyala/fastjson
 # github.com/valyala/fastrand v1.0.0
 github.com/valyala/fastrand
 # github.com/valyala/gozstd v1.6.2


Test patch:
diff --git a/lib/decimal/decimal_test.go b/lib/decimal/decimal_test.go
index 1dbe15e34..3bb41c612 100644
--- a/lib/decimal/decimal_test.go
+++ b/lib/decimal/decimal_test.go
@@ -7,6 +7,44 @@ import (
 	"testing"
 )
 
+func TestPositiveFloatToDecimal(t *testing.T) {
+	f := func(f float64, decimalExpected int64, exponentExpected int16) {
+		t.Helper()
+		decimal, exponent := positiveFloatToDecimal(f)
+		if decimal != decimalExpected {
+			t.Fatalf("unexpected decimal for positiveFloatToDecimal(%f); got %d; want %d", f, decimal, decimalExpected)
+		}
+		if exponent != exponentExpected {
+			t.Fatalf("unexpected exponent for positiveFloatToDecimal(%f); got %d; want %d", f, exponent, exponentExpected)
+		}
+	}
+	f(0, 0, 0)
+	f(1, 1, 0)
+	f(30, 3, 1)
+	f(12345678900000000, 123456789, 8)
+	f(12345678901234567, 12345678901234568, 0)
+	f(1234567890123456789, 12345678901234567, 2)
+	f(12345678901234567890, 12345678901234567, 3)
+	f(18446744073670737131, 1844674407367073, 4)
+	f(123456789012345678901, 12345678901234568, 4)
+	f(1<<53, 1<<53, 0)
+	f(1<<54, 1801439850948198, 1)
+	f(1<<55, 3602879701896396, 1)
+	f(1<<62, 4611686018427387, 3)
+	f(1<<63, 9223372036854775, 3)
+	f(1<<64, 18446744073709548, 3)
+	f(1<<65, 368934881474191, 5)
+	f(1<<66, 737869762948382, 5)
+	f(1<<67, 1475739525896764, 5)
+
+	f(0.1, 1, -1)
+	f(123456789012345678e-5, 12345678901234568, -4)
+	f(1234567890123456789e-10, 12345678901234568, -8)
+	f(1234567890123456789e-14, 1234567890123, -8)
+	f(1234567890123456789e-17, 12345678901234, -12)
+	f(1234567890123456789e-20, 1234567890123, -14)
+}
+
 func TestAppendDecimalToFloat(t *testing.T) {
 	testAppendDecimalToFloat(t, []int64{}, 0, nil)
 	testAppendDecimalToFloat(t, []int64{0}, 0, []float64{0})
@@ -248,8 +286,8 @@ func TestFloatToDecimal(t *testing.T) {
 
 	f(math.Inf(1), vInfPos, 0)
 	f(math.Inf(-1), vInfNeg, 0)
-	f(1<<63-1, 922337203685, 7)
-	f(-1<<63, -922337203685, 7)
+	f(1<<63-1, 9223372036854775, 3)
+	f(-1<<63, -9223372036854775, 3)
 
 	// Test precision loss due to conversionPrecision.
 	f(0.1234567890123456, 12345678901234, -14)