remix logo

Hacker Remix

Show HN: Floating point arithmetic types in C++ for any size and any base

79 points by seg_fault 4 days ago | 40 comments

Since there is so much interest on HN in floats lately and their software implementations, I wanted to show mine. It has no use and is just for teaching me floats and C++. Give me your thoughts.

listeria 1 day ago

dataflow 1 day ago

Why is std::is_object even specialized here? Isn't it always true regardless?

seg_fault 1 day ago

I don't get what you mean. I thought they specify how the type can be used?

badmintonbaseba 1 day ago

The cppreference page says:

> If the program adds specializations for std::is_fundamental or std::is_fundamental_v, the behavior is undefined.

This is an oversimplification. The actual rule is https://eel.is/c++draft/library#namespace.std-2 .

> the specialization meets the standard library requirements for the original template.

For is_fundamental<YourClassType> it means that is_fundamental<YourClassType>::value must be false, as YourClassType is not a fundamental type, as defined in https://eel.is/c++draft/basic.fundamental#17 .

Some traits are just not designed to be customization points.

secondcoming 1 day ago

Technically, you're not supposed to add your own specialisations to the `std` namespace

pmalynin 1 day ago

In general this isn’t true (i guess it is in this specific context). For example I believe it’s totally expected to specialize std hash

tyleo 1 day ago

I’ve also done this with hash… though given the footguns scattered about, I wouldn’t be surprised if it broke the spec.

Conscat 1 day ago

That is a completely intended way to use std::hash, along with a few other functions like std::tuple_size and std::tuple_element.

fuhsnn 1 day ago

For "any size" I was kind of expecting arbitrary sized mantissa/exponent, can be useful for emulating weird DACs, for example, 12-bit mantissa and 3-bit exponent[1].

[1] https://ajxs.me/blog/Yamaha_DX7_Technical_Analysis.html

seg_fault 1 day ago

Actually you can specify the numeric limits of the mantissa and the exponent. They can be specified as template arguments[0]. So you could do:

      Float<uint8_t, // type of the mantissa
            uint8_t, // type of the exponent
            0,       // lowest possible value of the mantissa
            4095,    // highest possible value of the mantissa
            0,       // lowest possible value of the exponent
            7>       // highest possible value of the exponent
The Float then simulates an unsigned 12bit mantissa and a 3bit exponent. Sure it still takes 16 bytes. But you could create a union with bitfields where you shrink that even further.

[0] https://github.com/clemensmanert/fas/blob/58f9effbe6c13ab334...

Archit3ch 1 day ago

Can you go in the other direction? Higher exponent and mantissa than regular float/double?

seg_fault 1 day ago

Sure.

    Float<int64_t, int64_t>
Gives you a signed Mantissa with 64 bit and a signed Exponent with 64bit. Since there are numeric limits for int64_t available, Float knows the max and the min value.

You could get even bigger ranges for Float by implementing your own big integer type.

badmintonbaseba 1 day ago

Possibly could be combined with C23's _BitInt(N) for the template arguments? I think it's available in clang as a C++ extension.

edit: or I guess you could have your own Tmantissa and Texponent types as custom classes that correctly model _BitInt(N), they don't seem to be required to be builtin integral types.

nly 1 day ago

seg_fault 12 hours ago

My type is a bit simpler. But I think the approach is the same. After all, boost's type has much more math functions implemented. I don't have exp, sqrt...

badmintonbaseba 1 day ago

Nice!

> TODO: (configurable) rounding support

What's the default rounding mode? Round to nearest even?

You might be interested in https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p33... too, which is a recent paper for introducing reproducible floating point to C++.

Very small floating point types can be handy for exhaustive testing of floating point function templates, especially ones that take multiple arguments. Walking over all floating point values for a small type often finds most if not all corner cases that can manifest with a floating point type of any size.

seg_fault 1 day ago

Rounding: actually it just cuts off. I have not spent much time to think about how to specify and implement the different rounding modes. Maybe some day...

Thanks for the hint to the paper. I also faced these issues. Thus, I provided a constructor which accepts mantissa and exponent as values. Very handy for the unittests.

badmintonbaseba 1 day ago

By cutting off do you mean that it correctly rounds towards zero? Maybe you can implement rounding to closest by just doing the calculation in a one digit wider mantissa with rounding to zero and observing the last digit, at least for an even base. It won't be rounding to even though, but for that a 2 digit wider mantissa is probably enough.

Rounding to nearest with an odd base doesn't seem to be as straightforwardly implementable from rounding to zero calculations at a higher precision.

seg_fault 1 day ago

I remember that I tried that some time ago. Especially the multiplication was tough, but I can not recall where I gave up. When I find some time, I will pick it up again :)