github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/encoding/float.go (about) 1 // Copyright 2014 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package encoding 12 13 import ( 14 "math" 15 16 "github.com/cockroachdb/errors" 17 ) 18 19 // EncodeFloatAscending returns the resulting byte slice with the encoded float64 20 // appended to b. The encoded format for a float64 value f is, for positive f, the 21 // encoding of the 64 bits (in IEEE 754 format) re-interpreted as an int64 and 22 // encoded using EncodeUint64Ascending. For negative f, we keep the sign bit and 23 // invert all other bits, encoding this value using EncodeUint64Descending. This 24 // approach was inspired by in github.com/google/orderedcode/orderedcode.go. 25 // 26 // One of five single-byte prefix tags are appended to the front of the encoding. 27 // These tags enforce logical ordering of keys for both ascending and descending 28 // encoding directions. The tags split the encoded floats into five categories: 29 // - NaN for an ascending encoding direction 30 // - Negative valued floats 31 // - Zero (positive and negative) 32 // - Positive valued floats 33 // - NaN for a descending encoding direction 34 // This ordering ensures that NaNs are always sorted first in either encoding 35 // direction, and that after them a logical ordering is followed. 36 func EncodeFloatAscending(b []byte, f float64) []byte { 37 // Handle the simplistic cases first. 38 switch { 39 case math.IsNaN(f): 40 return append(b, floatNaN) 41 case f == 0: 42 // This encodes both positive and negative zero the same. Negative zero uses 43 // composite indexes to decode itself correctly. 44 return append(b, floatZero) 45 } 46 u := math.Float64bits(f) 47 if u&(1<<63) != 0 { 48 u = ^u 49 b = append(b, floatNeg) 50 } else { 51 b = append(b, floatPos) 52 } 53 return EncodeUint64Ascending(b, u) 54 } 55 56 // EncodeFloatDescending is the descending version of EncodeFloatAscending. 57 func EncodeFloatDescending(b []byte, f float64) []byte { 58 if math.IsNaN(f) { 59 return append(b, floatNaNDesc) 60 } 61 return EncodeFloatAscending(b, -f) 62 } 63 64 // DecodeFloatAscending returns the remaining byte slice after decoding and the decoded 65 // float64 from buf. 66 func DecodeFloatAscending(buf []byte) ([]byte, float64, error) { 67 if PeekType(buf) != Float { 68 return buf, 0, errors.Errorf("did not find marker") 69 } 70 switch buf[0] { 71 case floatNaN, floatNaNDesc: 72 return buf[1:], math.NaN(), nil 73 case floatNeg: 74 b, u, err := DecodeUint64Ascending(buf[1:]) 75 if err != nil { 76 return b, 0, err 77 } 78 u = ^u 79 return b, math.Float64frombits(u), nil 80 case floatZero: 81 return buf[1:], 0, nil 82 case floatPos: 83 b, u, err := DecodeUint64Ascending(buf[1:]) 84 if err != nil { 85 return b, 0, err 86 } 87 return b, math.Float64frombits(u), nil 88 default: 89 return nil, 0, errors.Errorf("unknown prefix of the encoded byte slice: %q", buf) 90 } 91 } 92 93 // DecodeFloatDescending decodes floats encoded with EncodeFloatDescending. 94 func DecodeFloatDescending(buf []byte) ([]byte, float64, error) { 95 b, r, err := DecodeFloatAscending(buf) 96 if r != 0 && !math.IsNaN(r) { 97 // All values except for 0 and NaN were negated in EncodeFloatDescending, so 98 // we have to negate them back. Negative zero uses composite indexes to 99 // decode itself correctly. 100 r = -r 101 } 102 return b, r, err 103 }