Skip to content

Commit

Permalink
float16conv
Browse files Browse the repository at this point in the history
  • Loading branch information
lucix-aws committed Jan 12, 2024
1 parent 7faca30 commit 693c486
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 3 deletions.
63 changes: 61 additions & 2 deletions encoding/cbor/cbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
// the Smithy RPCv2-CBOR protocol and is NOT suitable for general application
// use.
//
// TODO: example
//
// As the encoding API operates strictly off of a constructed syntax tree, the
// length of each data item in a Value will always be known and the encoder
// will always generate definite-length encodings of container types (byte/text
Expand All @@ -18,6 +16,7 @@ package cbor

import (
"encoding/binary"
"math"
)

// MajorType enumerates CBOR major types.
Expand Down Expand Up @@ -79,6 +78,11 @@ var (
_ Value = List(nil)
_ Value = Map(nil)
_ Value = (*Tag)(nil)
_ Value = Major7Bool(false)
_ Value = (*Major7Nil)(nil)
_ Value = (*Major7Undefined)(nil)
_ Value = Major7Float32(0)
_ Value = Major7Float64(0)
)

// UInt describes a CBOR uint (major type 0).
Expand Down Expand Up @@ -189,23 +193,74 @@ func (t Tag) encode(p []byte) int {
// Major7Byte describes a boolean value (major type 7, argument 20/21).
type Major7Bool bool

func (b Major7Bool) len() int {
return 1
}

func (b Major7Bool) encode(p []byte) int {
if b {
p[0] = compose(MajorType7, major7True)

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.19)

cannot use MajorType7 (constant 7 of type MajorType) as type byte in argument to compose

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / API Diff Check

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.21)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.19)

cannot use MajorType7 (constant 7 of type MajorType) as type byte in argument to compose

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.20)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.21)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 202 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.20)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose
} else {
p[0] = compose(MajorType7, major7False)

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.19)

cannot use MajorType7 (constant 7 of type MajorType) as type byte in argument to compose

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / API Diff Check

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.21)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.19)

cannot use MajorType7 (constant 7 of type MajorType) as type byte in argument to compose

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.20)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.21)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose

Check failure on line 204 in encoding/cbor/cbor.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.20)

cannot use MajorType7 (constant 7 of type MajorType) as byte value in argument to compose
}
return 1
}

// Major7Nil is the `nil` / `null` literal (major type 7, argument 22).
type Major7Nil struct{}

func (*Major7Nil) len() int {
return 1
}

func (*Major7Nil) encode(p []byte) int {
p[0] = 0b_111_10110
return 1
}

// Major7Undefined is the `undefined` literal (major type 7, argument 23).
type Major7Undefined struct{}

func (*Major7Undefined) len() int {
return 1
}

func (*Major7Undefined) encode(p []byte) int {
p[0] = 0b_111_10111
return 1
}

// Major7Float32 describes an IEEE 754 single-precision floating-point number
// (major type 7, argument 26).
//
// Go does not natively support float16, all values encoded as such (major type
// 7, argument 25) must be represented by this variant instead.
type Major7Float32 float32

func (f Major7Float32) len() int {
return 5
}

func (f Major7Float32) encode(p []byte) int {
p[0] = 0b_111_11010
binary.BigEndian.PutUint32(p[1:], math.Float32bits(float32(f)))
return 5
}

// Major7Float64 describes an IEEE 754 double-precision floating-point number
// (major type 7, argument 27).
type Major7Float64 float64

func (f Major7Float64) len() int {
return 9
}

func (f Major7Float64) encode(p []byte) int {
p[0] = 0b_111_11011
binary.BigEndian.PutUint64(p[1:], math.Float64bits(float64(f)))
return 5
}

func encodeLen(t MajorType, ln int, p []byte) int {
if ln < 24 {
p[0] = byte(t)<<5 | byte(ln)
Expand Down Expand Up @@ -241,3 +296,7 @@ func getLen(ln int) int {
}
return 9 // type + 8-byte len
}

func compose(major, minor byte) byte {
return major << 5 & minor
}
32 changes: 31 additions & 1 deletion encoding/cbor/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func decode(p []byte) (Value, int, error) {
case MajorTypeTag:
return decodeTag(p)
default: // MajorType7
return nil, 0, fmt.Errorf("TODO: decodeMajor7()")
return decodeMajor7(p)
}
}

Expand Down Expand Up @@ -232,6 +232,36 @@ func decodeTag(p []byte) (*Tag, int, error) {
return &Tag{ID: id, Value: v}, off + n, nil
}

const (
major7False = iota + 0b_10100
major7True
major7Nil
major7Undefined
)

const (
major7Float16 = iota + 0b_11001
major7Float32
major7Float64
)

func decodeMajor7(p []byte) (Value, int, error) {
switch m := peekMinor(p); m {
case major7True, major7False:
return Major7Bool(m == major7True), 1, nil
case major7Nil:
return &Major7Nil{}, 1, nil
case major7Undefined:
return &Major7Undefined{}, 1, nil
case major7Float16:
case major7Float32:

return Major7Float32(), 5, nil

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.19)

missing argument in conversion to Major7Float32

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / API Diff Check

missing argument in conversion to Major7Float32

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.21)

missing argument in conversion to Major7Float32

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.19)

missing argument in conversion to Major7Float32

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.20)

missing argument in conversion to Major7Float32

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.21)

missing argument in conversion to Major7Float32

Check failure on line 259 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.20)

missing argument in conversion to Major7Float32
default:
return nil, 0, fmt.Errorf("unexpected minor value %d", minor)

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.19)

undefined: minor

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / API Diff Check

undefined: minor

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.21)

undefined: minor

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.19)

undefined: minor

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.20)

undefined: minor

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.21)

undefined: minor

Check failure on line 261 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.20)

undefined: minor
}
}

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.19)

missing return

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / API Diff Check

missing return

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.21)

missing return

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.19)

missing return

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (ubuntu-latest, 1.20)

missing return

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.21)

missing return

Check failure on line 263 in encoding/cbor/decode.go

View workflow job for this annotation

GitHub Actions / SDK Unit Tests (macos-latest, 1.20)

missing return

func peekMajor(p []byte) MajorType {
return MajorType(p[0] & 0b_111_00000 >> 5)
}
Expand Down
46 changes: 46 additions & 0 deletions encoding/cbor/float16.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cbor

// float16:
// sign|exp(5)|mant(10)
//
// float32:
// sign|exp(8)|mant(23)
func float16to32(f uint16) uint32 {
sign, exp, mant := splitf16(f)
if exp == 0x1f {
return sign | 0xff<<23 | exp // infinity/NaN
}

if exp == 0 {
if mant == 0 { // subnormal 0, but keep the exponent
return sign | (exp+127-15)<<23
}

// this is a float16 subnormal (true exponent -14)
// starting from there, we shift the mantissa over until we've
// chopped off the most-significant 1, i.e. that becomes the hidden
// mantissa bit and we're back in normal float32 space
exp = -14 + 127
for mant&0x800000 == 0 { // repeat until bit 24 is 1
mant <<= 1
exp--
}
mant &= 0x7FFFFF // remask to 23bit
} else {
exp += 127 - 15
}

return sign | exp<<23 | mant
}

// breaks a float16 down into its components:
// - sign, in float32 position
// - exponent, as a number (for bias shifting and subnormal conversion)
// - mantissa, in float32 position
func splitf16(f uint16) (sign, exp, mantissa uint32) {
const smask = 0b_1 << 15
const emask = 0b_11111 << 10
const mmask = 0b_1111111111

return uint32(f&smask) << 16, uint32(f&emask) >> 10, uint32(f&mmask) << 13
}

0 comments on commit 693c486

Please sign in to comment.