Provide support for half-precision floats
parent
d0ee8df1af
commit
332ca4af10
22
README.md
22
README.md
|
@ -5,22 +5,16 @@
|
||||||
This library implements the [RFC7049: Concise Binary Object Representation (CBOR)][rfc]
|
This library implements the [RFC7049: Concise Binary Object Representation (CBOR)][rfc]
|
||||||
in Crystal.
|
in Crystal.
|
||||||
|
|
||||||
|
**WARNING:** This library is still a work in progress.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Full support for diagnostic notation
|
- Full support for diagnostic notation
|
||||||
- Assign a field to a type base on the CBOR tag
|
- Assign a field to a type base on the CBOR tag
|
||||||
- Support for a wide range of IANA CBOR Tags
|
- Support for a wide range of IANA CBOR Tags (see below)
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
### Half-precision floating point is not supported
|
|
||||||
|
|
||||||
Crystal doesn't have a `Float16` type, so half-precision floating point numbers
|
|
||||||
are not supported for the time being.
|
|
||||||
|
|
||||||
If you know of a way to solve handle half-precision float, a contribution would
|
|
||||||
be really appreciated.
|
|
||||||
|
|
||||||
### Maximum Array/String array/Bytes array length
|
### Maximum Array/String array/Bytes array length
|
||||||
|
|
||||||
The spec allows for the maximum length of arrays, string arrays and bytes array
|
The spec allows for the maximum length of arrays, string arrays and bytes array
|
||||||
|
@ -49,6 +43,15 @@ require "cbor"
|
||||||
|
|
||||||
TODO: Write usage instructions here
|
TODO: Write usage instructions here
|
||||||
|
|
||||||
|
## Supported tags
|
||||||
|
|
||||||
|
All the tags specified in [section 2.4 of RFC 7049][rfc-tags] are supported
|
||||||
|
and the values are encoded in the respective Crystal types:
|
||||||
|
|
||||||
|
- `Time`
|
||||||
|
- `BigInt`
|
||||||
|
- `BigFloat`
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
TODO: Write development instructions here
|
TODO: Write development instructions here
|
||||||
|
@ -66,3 +69,4 @@ TODO: Write development instructions here
|
||||||
- [Alberto Restifo](https://github.com/your-github-user) - creator and maintainer
|
- [Alberto Restifo](https://github.com/your-github-user) - creator and maintainer
|
||||||
|
|
||||||
[rfc]: https://tools.ietf.org/html/rfc7049
|
[rfc]: https://tools.ietf.org/html/rfc7049
|
||||||
|
[rfc-tags]: https://tools.ietf.org/html/rfc7049#section-2.4
|
||||||
|
|
|
@ -3,19 +3,17 @@ require "./spec_helper"
|
||||||
# All those tests have been exported from the RFC7049 appendix A.
|
# All those tests have been exported from the RFC7049 appendix A.
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
# Disabled as half-precision floats are not supported:
|
|
||||||
{ %(0.0), "f9 00 00" },
|
{ %(0.0), "f9 00 00" },
|
||||||
{ %(-0.0), "f9 80 00" },
|
{ %(-0.0), "f9 80 00" },
|
||||||
{ %(1.0), "f9 3c 00" },
|
{ %(1.0), "f9 3c 00" },
|
||||||
{ %(1.5), "f9 3e 00" },
|
{ %(1.5), "f9 3e 00" },
|
||||||
{ %(65504.0), "f9 7b ff" },
|
{ %(65504.0), "f9 7b ff" },
|
||||||
# { %(0.00006103515625), "f9 04 00" }, TODO: Something about the presentation
|
{ %(6.1035156e-5), "f9 04 00" },
|
||||||
{ %(-4.0), "f9 c4 00" },
|
{ %(-4.0), "f9 c4 00" },
|
||||||
# { %(5.960464477539063e-8), "f9 00 01" },
|
{ %(5.9604645e-8), "f9 00 01" },
|
||||||
# { %(Infinity), "f9 7c 00" },
|
{ %(Infinity), "f9 7c 00" },
|
||||||
# { %(NaN), "f9 7e 00" },
|
{ %(NaN), "f9 7e 00" },
|
||||||
# { %(-Infinity), "f9 fc 00" },
|
{ %(-Infinity), "f9 fc 00" },
|
||||||
|
|
||||||
{ %(0), "00" },
|
{ %(0), "00" },
|
||||||
{ %(1), "01" },
|
{ %(1), "01" },
|
||||||
{ %(10), "0a" },
|
{ %(10), "0a" },
|
||||||
|
@ -36,7 +34,7 @@ tests = [
|
||||||
{ %(-1000), "39 03 e7" },
|
{ %(-1000), "39 03 e7" },
|
||||||
{ %(1.1), "fb 3f f1 99 99 99 99 99 9a" },
|
{ %(1.1), "fb 3f f1 99 99 99 99 99 9a" },
|
||||||
{ %(100000.0), "fa 47 c3 50 00" },
|
{ %(100000.0), "fa 47 c3 50 00" },
|
||||||
# { %(3.4028234663852886e+38), "fa 7f 7f ff ff" }, TODO: Not precise enough?
|
{ %(3.4028235e+38), "fa 7f 7f ff ff" },
|
||||||
{ %(1.0e+300), "fb 7e 37 e4 3c 88 00 75 9c" },
|
{ %(1.0e+300), "fb 7e 37 e4 3c 88 00 75 9c" },
|
||||||
{ %(-4.1), "fb c0 10 66 66 66 66 66 66" },
|
{ %(-4.1), "fb c0 10 66 66 66 66 66 66" },
|
||||||
{ %(Infinity), "fa 7f 80 00 00" },
|
{ %(Infinity), "fa 7f 80 00 00" },
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
# Returns a Float32 by reading the 16 bit as a IEEE 754 half-precision floating
|
# Reads the `UInt16` as a half-point floating point number
|
||||||
# point (Float16).
|
|
||||||
def Float32.new(i : UInt16)
|
def Float32.new(i : UInt16)
|
||||||
# Check for signed zero
|
# Check for signed zero
|
||||||
if i & 0x7FFF_u16 == 0
|
if i & 0x7FFF == 0
|
||||||
return (i.unsafe_as(UInt32) << 16).unsafe_as(Float32)
|
return (i.unsafe_as(UInt32) << 16).unsafe_as(Float32)
|
||||||
end
|
end
|
||||||
|
|
||||||
half_sign = (i & 0x8000_u16).unsafe_as(UInt32)
|
half_sign = (i & 0x8000).to_u32
|
||||||
half_exp = (i & 0x7C00_u16).unsafe_as(UInt32)
|
half_exp = (i & 0x7C00).to_u32
|
||||||
half_man = (i & 0x03FF_u16).unsafe_as(UInt32)
|
half_man = (i & 0x03FF).to_u32
|
||||||
|
|
||||||
# Check for an infinity or NaN when all exponent bits set
|
# Check for an infinity or NaN when all exponent bits set
|
||||||
if half_exp == 0x7C00_u32
|
if (i & 0x7C00) == 0x7C00
|
||||||
# Check for signed infinity if mantissa is zero
|
# Check for signed infinity if mantissa is zero
|
||||||
if half_man == 0
|
if half_man == 0
|
||||||
return ((half_sign << 16) | 0x7F80_0000_u32).unsafe_as(Float32)
|
return ((half_sign << 16) | 0x7F80_0000).unsafe_as(Float32)
|
||||||
else
|
else
|
||||||
# NaN, keep current mantissa but also set most significiant mantissa bit
|
# NaN, keep current mantissa but also set most significiant mantissa bit
|
||||||
return ((half_sign << 16) | 0x7FC0_0000_u32 | (half_man << 13)).unsafe_as(Float32)
|
return ((half_sign << 16) | 0x7FC0_0000 | (half_man << 13)).unsafe_as(Float32)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Calculate single-precision components with adjusted exponent
|
# Calculate single-precision components with adjusted exponent
|
||||||
sign = half_sign << 16
|
sign = half_sign << 16
|
||||||
# Unbias exponent
|
# Unbias exponent
|
||||||
unbiased_exp = ((half_exp.unsafe_as(Int32)) >> 10) - 15
|
unbiased_exp = (half_exp.unsafe_as(Int32) >> 10) - 15
|
||||||
|
|
||||||
# Check for subnormals, which will be normalized by adjusting exponent
|
# Check for subnormals, which will be normalized by adjusting exponent
|
||||||
if half_exp == 0
|
if half_exp == 0
|
||||||
|
@ -33,12 +32,12 @@ def Float32.new(i : UInt16)
|
||||||
|
|
||||||
# Rebias and adjust exponent
|
# Rebias and adjust exponent
|
||||||
exp = (127 - 15 - e) << 23
|
exp = (127 - 15 - e) << 23
|
||||||
man = (half_man << (14 + e)) & 0x7F_FF_FF_u32
|
man = (half_man << (14 + e)) & 0x7F_FF_FF
|
||||||
return (sign | exp | man).unsafe_as(Float32)
|
return (sign | exp | man).unsafe_as(Float32)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rebias exponent for a normalized normal
|
# Rebias exponent for a normalized normal
|
||||||
exp = (unbiased_exp + 127).unsafe_as(UInt32) << 23
|
exp = (unbiased_exp + 127).unsafe_as(UInt32) << 23
|
||||||
man = (half_man & 0x03FF_u32) << 13
|
man = (half_man & 0x03FF) << 13
|
||||||
(sign | exp | man).unsafe_as(Float32)
|
(sign | exp | man).unsafe_as(Float32)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue