Subject: Re: [Cbor] New pull request proposal for shortest float encoding

From: Laurence Lundblade <[email-masked]>
Date: Sun, 21 Oct 2018 10:40:22 +0530

So here’s Carsten’s latest text of float encoding from GitHub.

The preferred encoding for a floating point value is the shortest floating point encoding that preserves its value, e.g., 0xf94580 for the number 5.5, and 0xfa45ad9c00 for the number 5555.5, unless the CBOR-based protocol specifically excludes the use of the shorter floating point encodings. For NaN values, a shorter encoding is preferred if zero-padding the significand at the right reconstitutes the original NaN value (for many applications, the single NaN encoding 0xf97e00 will suffice).

After thinking about it some, I think this creates a decoding problem. 

If a decoder library receives a half-precision or single-precision float, the decoder does not know if it should promote that to a double or not when handing it up to the app using it. If the sender is using preferred encoding as described above, then the answer is yes. If not then it should not.  The decoder however has no way of knowing whether the encoder is using half-precision as part of preferred serialization or because the sender really wanted it to be half-precision.

I was OK with some ambiguity for a special option for shortest float that I proposed here <https://github.com/cbor-wg/CBORbis/pull/32>, but I do not think it is good for the front-and-center preferred encoding for floats.

One solution is the original pull request.  A tag could be added to make it clear. The tag could be on an enclosing map/array. It doesn’t need to be on every float.

Another similar is to have a tag indicating the sender is using preferred serialization.

Another is to make preferred serialization the base and have a tag that indicates otherwise. 

LL

> On Aug 28, 2018, at 2:28 AM, Laurence Lundblade <[email-masked]> wrote:
> 
> Sean, really glad you brought all this up and wrote it down. I was thinking some of it myself and it needs to be addressed.
> 
> Seems like we have a few options for indicating precision for shortest float encoding
> - Each CBOR protocol specification explicitly states that shortest float encoding is used for particular values. There is nothing explicit in the protocol messages
> - We use a tag like Thiago suggested
> - Both of the above are allowed
> 
> Since the tag takes up space, maybe the tag could be applied to maps and arrays to indicate all float data items in them are of a particular type. This would complicate the decoder as it has to keep track of some context at each nesting level.
> 
> 
> I don’t think the preference for shortest form that we apply to integers should apply to floats. Floats are fundamentally different from integers.  Floats are only approximations of the actual value. Precision tells you how good the approximation is which matters in some use cases.  If you encode 3 as 0x00000003 instead of 0x03 you don’t really get any more information, but there is a difference between 3.000 and 3.000000. That is why I propose that float and double are not subject to any shortest-form rule in the preferred serialization pull request <https://github.com/cbor-wg/CBORbis/pull/28>.
> 
> 
> I thought about leaving shortest float encoding entirely out of the RFC for most simplicity. If we leave it out, any CBOR protocol could still define it and use it. 
> 
> However, shortest float encoding does seems truly useful in reducing message sizes a lot for a lot of use cases. It also isn’t easy to figure out (at least it took me a while…), so I’m inclined to put it in.  Note that I believe the only purpose of shortest float encoding is to reduce message size. I see no value in it for canonicalization or for interop.  In fact it is extra complexity so it will hurt interop.
> 
> LL
> 
> 
> 
> 
>> On Aug 26, 2018, at 8:34 PM, Sean Solberg <[email-masked] <mailto:[email-masked]>> wrote:
>> 
>> Follow up question:  the proposal states:    Generally the decoder should promote half- and single-precision values to double-precision when they are received as it is implied that all half- and single-precision values transmitted are actually double precision. 
>> 
>> Why is it implied that a transmitted single precision value is actually a double precision value?   That makes no sense.  A single precision value is implied to be a single precision value in my mind.   If there was a "please treat as a double" flag sent along with a single precision value, then the transmitted data explicitly defines that the transmitted single precision value should be treated as a double precision value by the receiver.  without the flag, the single precision value transmitted should be treated by the receiver as a single precision value.  Explicit behavior is great.  Implicitly expecting a receiver to treat all floats (half, single & double) as doubles when received is implicitly a wrong choice.    
>> 
>> In general, any time things are done "implicitly", we end up with compatibility problems because people make different implicit choices/assumptions.  I believe keeping things explicit is a much better way to go.  
>> ..Sean
>> 
>> On Sun, Aug 26, 2018 at 10:25 PM, Sean Solberg <[email-masked] <mailto:[email-masked]>> wrote:
>> Question on this:  Even though the actual value transmitted has not lost any precision for that particular value, how does the receiver then know that the intended data type actually does have higher precision than what was transmitted?  Are there some flags somewhere that notify the receiver that it's actually a double instead of a single or its actually a single instead of a half?
>> 
>> ..Sean
>> 
>> On Sat, Aug 25, 2018 at 3:38 PM, Laurence Lundblade <[email-masked] <mailto:[email-masked]>> wrote:
>> https://github.com/cbor-wg/CBORbis/pull/32 <https://github.com/cbor-wg/CBORbis/pull/32>
>> 
>> This is a proposal to reduce CBOR message size without losing floating point precision by encoding the larger floating point values as smaller ones when it can be done without dropping any significant bits.
>> 
>> I personally don't think this should be part of canonicalization, or part of preferred encoding. It's just a very useful way of encoding floating points that seems worth including in the RFC.
>> 
>> LL
>> 
>> 
>> _______________________________________________
>> CBOR mailing list
>> [email-masked] <mailto:[email-masked]>
>> https://www.ietf.org/mailman/listinfo/cbor <https://www.ietf.org/mailman/listinfo/cbor>
>> 
>> 
>> 
>> _______________________________________________
>> CBOR mailing list
>> [email-masked] <mailto:[email-masked]>
>> https://www.ietf.org/mailman/listinfo/cbor
> 
> _______________________________________________
> CBOR mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/cbor

---

From: Sean Solberg <[email-masked]>
Date: Sun, 21 Oct 2018 18:00:26 -0500

IMHO,

I believe it's much more important to preserve explicit data types than it
is to try and squeak out every byte possible from the payload.  It's
already compact enough as it is without having to employ trickery to make
the payload as small as possible for a very small set set of rare values.
Keep It Simple:  serialize a single as a single and serialize a double as a
double and leave it at that.  No extra processing needed, no extra tags
needed, no ambiguity between the sender and receiver.  Explicit.  Done.
Reliable.

Just my opinion here...  Sean Solberg

On Sun, Oct 21, 2018 at 12:10 AM Laurence Lundblade <[email-masked]>
wrote:

> The preferred encoding for a floating point value is the shortest floating
> point encoding that preserves its value, e.g., 0xf94580 for the number 5.5,
> and 0xfa45ad9c00 for the number 5555.5, unless the CBOR-based protocol
> specifically excludes the use of the shorter floating point encodings. For
> NaN values, a shorter encoding is preferred if zero-padding the significand
> at the right reconstitutes the original NaN value (for many applications,
> the single NaN encoding 0xf97e00 will suffice).
>
>
> If a decoder library receives a half-precision or single-precision float,
> the decoder does not know if it should promote that to a double or not when
> handing it up to the app using it. If the sender is using preferred
> encoding as described above, then the answer is yes. If not then it should
> not.  The decoder however has no way of knowing whether the encoder is
> using half-precision as part of preferred serialization or because the
> sender really wanted it to be half-precision.
>
> I was OK with some ambiguity for a special option for shortest float that
> I proposed here <https://github.com/cbor-wg/CBORbis/pull/32>, but I do
> not think it is good for the front-and-center preferred encoding for floats.
>
> One solution is the original pull request.  A tag could be added to make
> it clear. The tag could be on an enclosing map/array. It doesn’t need to be
> on every float.
>
> Sean, really glad you brought all this up and wrote it down. I was
> thinking some of it myself and it needs to be addressed.
>
> - Each CBOR protocol specification explicitly states that shortest float
> encoding is used for particular values. There is nothing explicit in the
> protocol messages
> Since the tag takes up space, maybe the tag could be applied to maps and
> arrays to indicate all float data items in them are of a particular type.
> This would complicate the decoder as it has to keep track of some context
> at each nesting level.
>
>
> I don’t think the preference for shortest form that we apply to integers
> should apply to floats. Floats are fundamentally different from integers.
> Floats are only approximations of the actual value. Precision tells you how
> good the approximation is which matters in some use cases.  If you encode 3
> as 0x00000003 instead of 0x03 you don’t really get any more information,
> but there is a difference between 3.000 and 3.000000. That is why I propose
> that float and double are not subject to any shortest-form rule in the preferred
> serialization pull request <https://github.com/cbor-wg/CBORbis/pull/28>.
>
>
> I thought about leaving shortest float encoding entirely out of the RFC
> for most simplicity. If we leave it out, any CBOR protocol could still
> define it and use it.
>
> However, shortest float encoding does seems truly useful in reducing
> message sizes a lot for a lot of use cases. It also isn’t easy to figure
> out (at least it took me a while…), so I’m inclined to put it in.  Note
> that I believe the only purpose of shortest float encoding is to reduce
> message size. I see no value in it for canonicalization or for interop.  In
> fact it is extra complexity so it will hurt interop.
>
> LL
>
>
>
>
> On Aug 26, 2018, at 8:34 PM, Sean Solberg <[email-masked]> wrote:
>
> Follow up question:  the proposal states:   Generally the decoder should
> promote half- and single-precision values to double-precision when they
> are received as it is implied that all half- and single-precision values
> transmitted are actually double precision. Why is it implied that a
> transmitted single precision value is actually a double precision value?
>  That makes no sense.  A single precision value is implied to be a single
> precision value in my mind.   If there was a "please treat as a double"
> flag sent along with a single precision value, then the transmitted data
> explicitly defines that the transmitted single precision value should be
> treated as a double precision value by the receiver.  without the flag, the
> single precision value transmitted should be treated by the receiver as a
> single precision value.  Explicit behavior is great.  Implicitly expecting
> a receiver to treat all floats (half, single & double) as doubles when
> received is implicitly a wrong choice.
>
> In general, any time things are done "implicitly", we end up with
> compatibility problems because people make different implicit
> choices/assumptions.  I believe keeping things explicit is a much better
> way to go.
> ..Sean
>
> On Sun, Aug 26, 2018 at 10:25 PM, Sean Solberg <[email-masked]> wrote:
>
>> Question on this:  Even though the actual value transmitted has not lost
>> any precision for that particular value, how does the receiver then know
>> that the intended data type actually does have higher precision than what
>> was transmitted?  Are there some flags somewhere that notify the receiver
>> that it's actually a double instead of a single or its actually a single
>> instead of a half?
>>
>> ..Sean
>>
>> On Sat, Aug 25, 2018 at 3:38 PM, Laurence Lundblade <
>> [email-masked]> wrote:
>>
>>> https://github.com/cbor-wg/CBORbis/pull/32
>>>
>>> This is a proposal to reduce CBOR message size without losing floating
>>> point precision by encoding the larger floating point values as smaller
>>> ones when it can be done without dropping any significant bits.
>>> I personally don't think this should be part of canonicalization, or
>>> part of preferred encoding. It's just a very useful way of encoding
>>> floating points that seems worth including in the RFC.
>>>
>>> LL
>>>
>>>

---

From: Carsten Bormann <[email-masked]>
Date: Mon, 22 Oct 2018 08:26:40 +0200

The operative question here is whether the three IEEE 754 floating point representations represent different types at the generic data model level or not.  So far, we have had the position that they don’t.  You seem to be suggesting that they do.

Please also note that the introduction says:

   4.  The serialization must be reasonably compact, but data
       compactness is secondary to code compactness for the encoder and
       decoder.

       *  "Reasonable" here is bounded by JSON as an upper bound in
          size, and by implementation complexity maintaining a lower
          bound.  Using either general compression schemes or extensive
          bit-fiddling violates the complexity goals.

5.5 in JSON is three bytes; 0xf94580 is, too.  0xfb4016000000000000 isn’t, but that would be the equivalent in CBOR to what 5.5 is in I-JSON if binary64 were a different type than binary16.  More generally, most power-affluent implementations will naturally take an incoming 0xf94580 from a sensor into further processing as binary64 (or possibly binary32), so any distinction at the data model level would put an additional burden on the processing.

If precision does need to be indicated for a value, this should be done via a tag.  And that shouldn’t just allow for the resolution afforded by 11-bit, 24-bit, or 53-bit mantissae.  (We don’t have to specify this tag in CBORbis — actually, we shouldn’t, as this would be a new invention.)

Grüße, Carsten

> On Oct 22, 2018, at 01:00, Sean Solberg <[email-masked]> wrote:
> 
> IMHO,
> 
> I believe it's much more important to preserve explicit data types than it is to try and squeak out every byte possible from the payload.  It's already compact enough as it is without having to employ trickery to make the payload as small as possible for a very small set set of rare values.  Keep It Simple:  serialize a single as a single and serialize a double as a double and leave it at that.  No extra processing needed, no extra tags needed, no ambiguity between the sender and receiver.  Explicit.  Done.  Reliable. 
> 
> On Sun, Oct 21, 2018 at 12:10 AM Laurence Lundblade <[email-masked]> wrote:
> I was OK with some ambiguity for a special option for shortest float that I proposed here, but I do not think it is good for the front-and-center preferred encoding for floats.
> 
> One solution is the original pull request.  A tag could be added to make it clear.. The tag could be on an enclosing map/array. It doesn’t need to be on every float.
> 
>> I don’t think the preference for shortest form that we apply to integers should apply to floats. Floats are fundamentally different from integers.  Floats are only approximations of the actual value. Precision tells you how good the approximation is which matters in some use cases.  If you encode 3 as 0x00000003 instead of 0x03 you don’t really get any more information, but there is a difference between 3.000 and 3.000000. That is why I propose that float and double are not subject to any shortest-form rule in the preferred serialization pull request.
>> 
>> 
>>> On Sat, Aug 25, 2018 at 3:38 PM, Laurence Lundblade <[email-masked]> wrote:

---

From: Laurence Lundblade <[email-masked]>
Date: Mon, 22 Oct 2018 12:11:25 +0530

Yes, I think we are suggesting they are different types.

This is because conversation between them is not straightforward like it is with integers. It is harder to implement, but more importantly the semantics for interoperability of the current text is not (yet) clear (to me). 

For doubles and singles with equal distribution of values, the saving will be small and inconsequential. But for some use cases, the saving will be very large. 

My proposal was to treat them as different types for preferred encoding, but also provide an optional specification for doing shortest encoding if a CBOR-based protocol decided it wanted that. 

LL

> On Oct 22, 2018, at 11:56 AM, Carsten Bormann <[email-masked]> wrote:
>

---

From: Michael Richardson <[email-masked]>
Date: Mon, 22 Oct 2018 14:24:16 -0400

> If a decoder library receives a half-precision or single-precision
    > float, the decoder does not know if it should promote that to a double
    > or not when handing it up to the app using it. If the sender is using
    > preferred encoding
    > as described above, then the answer is yes. If not then it should
    > using
    > half-precision as part of preferred serialization or because the sender
    > really wanted it to be half-precision.

This seems like a protocol problem to me, not a decoder problem.

If the protocol makes a meaningful difference between:
    0.5
and 0.500
(i.e. between 5.0e-1 +- 0.1, and 5.0e-1 +- 0.001)
{and there are a lots of situations where these are different}

then the protocol should include error bars rather than trying cheet with
encoding tricks.

--
Michael Richardson <[email-masked]>, Sandelman Software Works
 -= IPv6 IoT consulting =-

---

From: Carsten Bormann <[email-masked]>
Date: Mon, 22 Oct 2018 20:26:34 +0200

> then the protocol should include error bars rather than trying cheet with

+1e3(±100)

Grüße, Carsten

---

From: Michael Richardson <[email-masked]>
Date: Mon, 22 Oct 2018 15:25:01 -0400

We had some discussion last Wednesday about the question of how
CBOR specification was neutral as to whether encoders or decoders
would bear the burden being "correct".

There are two situations which I think that Carsten has spoken of
several times, but I'll recap:

1) a protocol in which it is known that no integer will be bigger
   than X (maybe X=1_000_000_000), and so larger encodings and
   decodings are not needed.  This can have a code space affect on the
   encoder, as it must use shorter (or shortest) encodings.
   On the decoder, the guarantee means that it can depend upon never
   seeing the number 10 encoded using a 64-bit integer, and save code.
   (which is legal!)
   If such a thing occurs, then things break.

2) a protocol in which, despite knowing that there might be limits in
   practice, all representations are available.
   The encoder can then do things like just lay out CBOR into memory,
   assuming largest integer sizes possible, and then just fill in the
   actual numbers once they are known, even though there could be
   encoded using shorter encodings.

While I am happy that CBOR remain neutral, I am suggesting that these two
cases should get names, and that the CBOR document should explain these.
This will let people annotate their CBOR libraries as to their capabilities,
possibly making compile flags more meaningful across implementations.

In particular a case for (1) is where it is known that floats are never
used should probably be stated.

---

From: Carsten Bormann <[email-masked]>
Date: Tue, 23 Oct 2018 01:07:17 +0200

Hi Michael,

Great input.  I tried to invent some words; please have a look at

https://github.com/cbor-wg/CBORbis/commit/54d7a5292a389405436fe97deeaae466630b9e68

Grüße, Carsten

> On Oct 22, 2018, at 21:25, Michael Richardson <[email-masked]> wrote:
> 
> Signed PGP part
>

---

From: Michael Richardson <[email-masked]>
Date: Mon, 22 Oct 2018 19:47:15 -0400

Thanks for that patch. So:
        - preferred encoder
        - variant encoder
        - variation-tolerant decoder

are the new terms?  I can't say that they are wrong, as they are perfectly
reasonable descriptions of what you are defining.

I'm not sure if I can use this when describing my (hypothetical) CBOR library
code.

-DINTEGER_VARIANT_ENCODER
vs
-DINTEGER_PREFERRED_ENCODER

---

From: Carsten Bormann <[email-masked]>
Date: Tue, 23 Oct 2018 01:52:53 +0200

On Oct 23, 2018, at 01:47, Michael Richardson <[email-masked]> wrote:
> are the new terms?  

Those are the ones I came up with.

> I can't say that they are wrong, as they are perfectly

That indeed makes little sense, because the code would need to know what representation variants to leave out.

-DINTEGER_VARIANT_ENCODER_32BIT_ONLY

is closer.

> -DINTEGER_PREFERRED_ENCODER

While this would make sense (except that it should be the default…).

Grüße, Carsten

---

From: Michael Richardson <[email-masked]>
Date: Tue, 23 Oct 2018 09:18:49 -0400

> That indeed makes little sense, because the code would need to know
    > what representation variants to leave out.

    > is closer.

Agreed.

    > While this would make sense (except that it should be the default…).

Also agreed.

---

From: Laurence Lundblade <[email-masked]>
Date: Tue, 23 Oct 2018 18:43:38 +0530

OK. Agree with you on this now. Thanks for listening.  (I realized my encoding API for integers always takes an Int64 and decoding always gives an Int64 even though it does preferred encoding of ints. If I’m happy with that I have to be happy with the same with a double.)

On to another thing…. There’s new text on NaNs:
For NaN values, a shorter encoding is preferred if zero-padding the shorter significand towards the right reconstitutes the original NaN value (for many applications, the single NaN encoding 0xf97e00 will suffice).

I read this is the same idea for NaN payloads. If you can make it small without losing NaN payload bits, it is OK to do it. Seems like the right thing to do.

I did an experiment and on the x86 in my MacPro laptop.  Every NaN is a qNaN. The CVTSD2SS instruction ignores the NaN payload and even converts an sNaN to a qNaN. The new text also won’t preserve the typical sNaN which is just the LSB set. 

Since NaN payloads are really obscure, I’m not sure how much this really matters, but here’s a suggestion:
For NaN values, a shorter encoding is preferred if zero-padding the shorter significand towards the right reconstitutes the original NaN value. An implementation may also choose not to support NaN payloads and sNaN (signaling NaN), in which case NaN can always be encoded as 0xf97e00.

LL

> On Oct 22, 2018, at 11:54 PM, Michael Richardson <[email-masked]> wrote:
> 
>

---

From: Carsten Bormann <[email-masked]>
Date: Tue, 23 Oct 2018 21:11:59 +0200

> For NaN values, a shorter encoding is preferred if zero-padding the shorter significand towards the right reconstitutes the original NaN value. An implementation may also choose not to support NaN…

Thanks!

When you say “implementation”, you mean the encoder, right?
I wonder what a decoder should do with an incoming sNaN or NaN payload.

Grüße, Carsten

---

From: Laurence Lundblade <[email-masked]>
Date: Wed, 24 Oct 2018 08:49:13 +0530

> On Oct 24, 2018, at 12:41 AM, Carsten Bormann <[email-masked]> wrote:
> 

Yes

> I wonder what a decoder should do with an incoming sNaN or NaN payload.

If the decoder just passes the float along in the size it received it, then the payload will come along.  If the decoder widens it to double then it will depend on what the implementation of the widening does. On x86, it will strip it. 

So change “implementation” to “encoder / decoder”?

LL