diff --git a/README.md b/README.md index d5cdd0b..abfb633 100644 --- a/README.md +++ b/README.md @@ -5,22 +5,16 @@ This library implements the [RFC7049: Concise Binary Object Representation (CBOR)][rfc] in Crystal. +**WARNING:** This library is still a work in progress. + ## Features - Full support for diagnostic notation - 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 -### 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 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 +## 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 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 [rfc]: https://tools.ietf.org/html/rfc7049 +[rfc-tags]: https://tools.ietf.org/html/rfc7049#section-2.4 diff --git a/spec/rfc_spec.cr b/spec/rfc_spec.cr index 6eaace6..c052b25 100644 --- a/spec/rfc_spec.cr +++ b/spec/rfc_spec.cr @@ -3,19 +3,17 @@ require "./spec_helper" # All those tests have been exported from the RFC7049 appendix A. tests = [ - # Disabled as half-precision floats are not supported: { %(0.0), "f9 00 00" }, { %(-0.0), "f9 80 00" }, { %(1.0), "f9 3c 00" }, { %(1.5), "f9 3e 00" }, { %(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" }, - # { %(5.960464477539063e-8), "f9 00 01" }, - # { %(Infinity), "f9 7c 00" }, - # { %(NaN), "f9 7e 00" }, - # { %(-Infinity), "f9 fc 00" }, - + { %(5.9604645e-8), "f9 00 01" }, + { %(Infinity), "f9 7c 00" }, + { %(NaN), "f9 7e 00" }, + { %(-Infinity), "f9 fc 00" }, { %(0), "00" }, { %(1), "01" }, { %(10), "0a" }, @@ -36,7 +34,7 @@ tests = [ { %(-1000), "39 03 e7" }, { %(1.1), "fb 3f f1 99 99 99 99 99 9a" }, { %(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" }, { %(-4.1), "fb c0 10 66 66 66 66 66 66" }, { %(Infinity), "fa 7f 80 00 00" }, diff --git a/src/cbor/type/float_16.cr b/src/cbor/type/float_16.cr index fa383e9..f81e26d 100644 --- a/src/cbor/type/float_16.cr +++ b/src/cbor/type/float_16.cr @@ -1,30 +1,29 @@ -# Returns a Float32 by reading the 16 bit as a IEEE 754 half-precision floating -# point (Float16). +# Reads the `UInt16` as a half-point floating point number def Float32.new(i : UInt16) # Check for signed zero - if i & 0x7FFF_u16 == 0 + if i & 0x7FFF == 0 return (i.unsafe_as(UInt32) << 16).unsafe_as(Float32) end - half_sign = (i & 0x8000_u16).unsafe_as(UInt32) - half_exp = (i & 0x7C00_u16).unsafe_as(UInt32) - half_man = (i & 0x03FF_u16).unsafe_as(UInt32) + half_sign = (i & 0x8000).to_u32 + half_exp = (i & 0x7C00).to_u32 + half_man = (i & 0x03FF).to_u32 # 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 if half_man == 0 - return ((half_sign << 16) | 0x7F80_0000_u32).unsafe_as(Float32) + return ((half_sign << 16) | 0x7F80_0000).unsafe_as(Float32) else # 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 # Calculate single-precision components with adjusted exponent sign = half_sign << 16 # 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 if half_exp == 0 @@ -33,12 +32,12 @@ def Float32.new(i : UInt16) # Rebias and adjust exponent 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) end # Rebias exponent for a normalized normal 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) end