the Fiddler's picture

OpenTK.Math.Half

Project:The Open Toolkit library
Component:Code
Category:feature request
Priority:normal
Assigned:Inertia
Status:closed
Description

This type should provide an interface similar to IntPtr, with a methods, conversion operators and constructors that can pack/unpack floats and doubles.

I'm opening this task so we can keep track of progress.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

#31

[Texture]
This should work without any modification whatsoever (need to test, though).

Inertia's picture

#32

[Serialization]
The interface is implemented and works, but the resulting file is quite big. Using 53 bytes for one 8 bytes array element is alot.

Half4 - 299 Bytes
Half4[1] - 336 Bytes
Half4[2] - 389 Bytes
Half4[3] - 442 Bytes

I will not remove the associated methods - they might prove useful in scenarios where efficiency is irrelevant - but the whole point of the Half format is to save memory by using a more compact representation of the number ....

[public bitfield]
So what's the problem again with having the ushort public?

Pro:
-least hassle with load/save.
-direct access for pinning.

Con:
-naive user might break it.

Who would use the Half type beside me anyway? Noone has shown any interest, so I think it's viable to assume that the bitfield can be made public for convenience - instead of trying to protect someone who doesn't use this type. I cannot get rid of the feeling that we're trying to design a tampon box that males would want to buy.

Adding SetBinary accessor&mutator would result in the same situation as having the field public? It doesn't matter to what value you set the ushort, none will cause a hardware crash. It's just that the ushort 123 is not 123.0f.

the Fiddler's picture

#33

Attachment?

[Serialization]
You tested xml serialization, right? This has significant overhead (it decorates every single field with its name), but fortunately it's highly compressible.

If you need a more compact representation on disk, you'll probably have to use binary serialization or a custom file format.

[Public bitfield]
It's information that you don't need in order to use Half. Single, Double, IntPtr don't expose their internal representations either - they are not necessary in order to use them.

-direct access for pinning.
Half's a struct, no pinning necessary. Relevant GL methods will have the necessary overloads to take either single or array of Halfs, so that's not a problem either.

-least hassle with load/save.
With floats / doubles you'd use the BitConverter class, or you'd write:

ushort s = 123;
Half h = *(Half*)&ushort_value;
ushort t = *(ushort*)&h;

But ok, I give in. Feel free to add two static methods which return a Half and a ushort respectively - but please, please, don't make the bitfield public!

This is information hiding 101, the user shouldn't know or care about the internal representation. Make the bitfield public and how much time till someone tries to add two bitfields together?

Inertia's picture

#34

Attachment?
None, the internal field is currently public because of equality comparison for debugging. I'll change it back and post it when I'm done.

[Serialization]
Used a binary formatter.

I've added 2 more methods that help keeping the field hidden:

public void FromBinaryStream(BinaryReader bin)
public void ToBinaryStream(BinaryWriter bin)

[Public bitfield]
Although it might not look like it, but I'm on your side on this one. I don't want it to be public either, although I'm curious what kind of bugs users will find if they just add the bitfields together. Think of the fun we would have if someone tries multiplication with bit shifts! ;)

Just trying to assess the situation. The type is not intended for arithmetic use, and primary usage is VBO and Textures. If you say that GL is not a problem I'll just add more To/From methods until we covered all load/save desires and the field can remain hidden. Any suggestions how someone would want to save/load it besides serialization and binary? XML makes not much sense, you need to convert to float for that.

the Fiddler's picture

#35

Any suggestions how someone would want to save/load it besides serialization and binary?
Strings:

public override string ToString(...);
public static Half Parse(string s, System.Globalization.NumberStyles style, IFormatProvider provider);
public static bool TryParse(string s, System.Globalization.NumberStyles style, IFormatProvider provider, out float);

We can route these through the Single class, so the implementation is not a problem.

Taking a hint from the BitConverter class, we could add like these:

public static byte[] GetBytes(Half half);
public static Half FromBytes(byte[] value, int startIndex);

The GetBytes()/FromBytes() methods are probably better than returning a ushort:

  • They are low-level enough that they can be used with any Stream.
  • Ushort is affected by endianess (not sure if Half is?)

Between BinaryReader/Writer, bytes and strings, I think we are pretty much covered on this front.

Inertia's picture

#36

Cleaned up the mess, and added operator overloads so you can do stuff like:

Vector4 v4 = new Vector4();
Half4 h4 = (Half4)v4;

The API between Half and Half4 is consistent now and interoperability with Vector4(d) should be sufficient too. I will not touch this anymore today, you probably want to fiddle with it anyways and I most certainly have marked the wrong overloads in Half4 being not CLS compliant (favored the ref/out ones as usual) . Unless you find any flaws I'd say the API is sound, or as sound as it can be without integration into OpenTK and writing some test applications that use the type in VBO and as Texture.

Converting a 16-Bit Half floating point to 16-Bit unicode strings gives me the shivers and I would refuse to do this even if my life depended on it, but I'll gladly add BitConverter-style methods.

AttachmentSize
OpenTK Half v6.rar9.68 KB
the Fiddler's picture

#37

Thanks, I'll make sure that there are no obvious issues and push it to svn tonight.

Converting a 16-Bit Half floating point to 16-Bit unicode strings gives me the shivers and I would refuse to do this even if my life depended on it, but I'll gladly add BitConverter-style methods.
Localization issues aside, it would be crazy to write this by hand you can do:

public override string ToString()
{
    return ((float)this).ToString();
}
Inertia's picture

#38

I'm not worried about the implementation, but the idea to convert a 16-Bit Half number to a format that uses 16-Bit per digit.

Regarding svn, it might be better to write an email to the responsible OpenEXR authors first before commiting, just to avoid any misunderstandings. That's why I've avoided svn so far, it became obvious when you posted v2.5 and I had to merge changes manually.

The endian problem should also be confirmed existant or void, before svn. Not sure how far .Net abstracts this problem away, don't have anything else besides little endian to test against so I can neither confirm nor reject the possibility.

the Fiddler's picture

#39

I'm not worried about the implementation, but the idea to convert a 16-Bit Half number to a format that uses 16-Bit per digit.
Not sure I follow you here..?

Console.WriteLine(135.0f);
Console.WriteLine(new Half(135.0f));

This should print 135.0f as expected, not OpenTK.Math.Half.

[License]
OpenEXR comes under the modified BSD license, which is pretty similar to our own (indeed, our MIT/X11 is equivalent to a 2-clause BSD license, while OpenEXR uses a 3-clause variant). According to the terms (simplified):

  1. We have to include the license in the source (already done) and in binary distributions (no big deal, we already do that for Mono and Prebuild).
  2. We may not use the ILM name to promote OpenTK without written consent (no problem - that's where our licenses differ).

[Endianess]
AFAIK, .Net doesn't make any guarantees about endianess - It provides a method to detect that (BitConverter.IsLittleEndian()), but that's it. This is something that we should resolve, but I don't think it should stop us from committing to svn. Rationale: OpenTK is not tested on big-endian platforms. Once someone does that, we can fix any problems we find.

That said, I'm looking for the IEEE754 specs.

Edit: Some more information on the endianess of floating point numbers. Quoting:

First, there is no official standard for endian-ness when transmitting IEEE floating point data over the wire. [...] C# defaults to host-endian format (always little-endian in practice, as the Mono guys have learned.) for BinaryReader and BinaryWriter. First point of fun.


Secondly, IEEE 754 floating point representation defines an entire range of values to represent NaN, not a single value. [...] C# allows whatever cruft happens to be in the value on the CPU to flow through to your binary representation. And don't assume in C# that double.NaN has all those set to 0. It doesn't. In practice, double.NaN in C# is full of cruft.

Inertia's picture

#40

[Half.ToString()]
I misunderstood you, thought you wanted that for saving to a file. We should definitely add ToString/Parse, it will hide alot of explicit casts. I just found the idea so crazy because if you convert Math.Pi into a Half you get something like 3.1406. Converting that into a string that is larger (in bytes) than a Double is just weird :P

[License]
That's how I interpreted it aswell, but you know my poor skills to read lawyer stuff. Well, if you think it's ready then commit it.

[Endianess]
This is good news since OpenTK requires .Net or Mono to run, endian problems with OpenTK will never arise until Mono is ported to a platform that is not little endian.

[NaN]
Thanks for the reminder, there are at least 2 Half values that are NaN. Going to find out if there's more.