Fixed-point arithmetic as a replacement for soft floats
pigweed.dev
pigweed.dev
https://github.com/PetteriAimonen/libfixmath
I saw this getting forked for a custom SDK for the PS1 (which doesnt have a FPU).
In the fixed code I used straight c but wrote functions for sin, cos, and reciprocal square root.
I can't see getting just a 2x improvement over soft float, while using llvm specific features and modifying libraries just eliminates portability.
Maybe I wasn't clear. I know it has an FPU. I originally wrote the code in fixed point because I knew it would be fast and didn't trust the FPU performance on such a small chip. Now I converted it to float and get the same performance.
I also used an "f" suffix on all constants to avoid accidental software double precision. Also used the GCC flags to force it all to single precision and use the hard float libs.
On smaller chips I used to expect fixed point to be faster than a hardware FPU, but that's no longer the case. They are about equal for me.
What kind of chip do you have in mind where that would be true?
Notably, M4 is the first of its line to have an FPU option at all.
[1]: https://s-o-c.org/cortex-m4f-vs-m4-how-do-these-arm-cores-co...
Rational arithmetic operations are just plain slow, and there isn't a lot of magic that can fix that.
With 32-bit integers, overflows caused by omitting the normalization step may be so frequent as to remove any advantage from delaying the normalization.
It is probably best to have a fixed-point operation library that implements both options for normalization, so you may test an application with both delayed and immediate normalization, and choose which is better.
They cannot be represented by floats either. So this doesn't matter.
I'm sorry, I have some bad news for you... especially the "evenly" part. I had a friend who worked at IBM research on the problem of "normalizing" some matrix multiplication irregularities which occur due to numerical underflow or overflow. And, it's anything but a solved or an easy problem.
And indeed it's well-understood that it can get problematic if you compose a bunch of such, esp. add/sub with a large magnitude difference. Indeed, especially matrix multiplication.
- often you use decimal fraction on output and input anyway
- it's slower, even slower with bigint
- no square root, sin, and so on with rationals
Better statement would be "no standard definition possible for square root, sin, and so on with rationals", as rationals with unbounded denominators don't have an inherent precision limit---something like ulps in floating point systems---and the max denominator wouldn't be a good choice for precision even when denominator is limited. Every such function now has to be given an explicit denominator value or similar strategy, which complicates both the interface and implementation.
Are you thinking about a specific library? You aren't the only person who commented this way. But, the truth is that root, sin and so on don't "work" with floats either. In fact, there are common ways to implement these functions by either using tables (which are approximate) or algebraic approximations (that give you... drum roll: rationals!)
But, really, there isn't any way (except symbolically) to represent transcendental functions in computers. It doesn't matter what kind of number you choose to do it.
all irrational operations (sqrt, sin, cos and so on) return Num in a consistent way
just to mention that the Raku numeric also improves over the Lisp/Scheme number tower? why - because truly a Num is _not_ a subset / child class of Rat, they are sets that intersect
My point being, your point doesn't actually make Raku sound like they made a good design decision, it’s just different for the sake of being different when put that way.
Let me try again. My case is that Num (ie P754 floating point) is not well aligned to decimals that humans use in most everyday circumstances. Thus the famous (0.1+0.2==0.3) example which works with Rat and breaks with Num.
The only benefit of a Num over Rat is performance and the disbenefit is that Num has limited precision and range. Unless you are talking about irrational numbers, when you have to have imprecision anyway.
Therefore having Rat as the default means that Num is an option for the coder to increase execution speed, but only if they accept the implications for precision and range.
Personally I'd argue that for most people, writing most applications, IEEE floats are a fantastic tradeoff between performance, memory, error, and stability. That's why they've stuck around as the dominant numerical representation for decades. For most of the situations where they suffer (e.g. rounding transcendental results, operands with widely varying orders of magnitude, results very near 0) don't have general solutions in other systems, so the middle ground between "safe for a naive programmer" and "requires expertise in numerical analysis" becomes very small overall.
IEEE floats are fine for most cases, but sometimes you want to tune up the accuracy and with IEEE floats that's typically not possible, and it's like you're in a dead end street.
Therefore I still think bignums are a better default choice for most programming languages.
Yes, and that's true of all arbitrary-sized types vs fixed-size types. There's a lot of costs that come with that choice, like inherent allocations, losing constant time operations, lack of hardware support, etc.
You can do exactly the same thing with floating point, for example GNU MPFR and ARPREC. Doing so is broadly faster than rational representations for a lot of common types of computations, and much less prone to size explosions. Still has the issue of incorrectly terminated representations at a given size or precision, but they can also represent things like irrationals far more efficiently.
That said, I've very rarely run into situations that couldn't be made to fit in a double or quad where rationals would be better. These types can represent the distances between planets down to the nanometer or micrometer scale without losing precision. If you're dealing with atomic manufacturing on an astronomical scale, it might be worth investing in a new type, but anything short of that is very likely to be a problem requiring domain expertise to evaluate. Floats are perfectly fine as a default.
The other advantage of floats is that they allow/require you to think about precision and rounding because they're a core part of the representation. Some people don't like that, but going to rational bignums doesn't free you of that burden.
Yes, you can use floats to build floats with more precision, but that's not the point. The point is to have correctness at your fingertips, rather than a datatype that comes with lots of caveats.
I agree with you that people who work with numerical algorithms might want to use floats, but programming languages with a general audience probably should have an arbitrary-precision datatype as a default.
That's why IEEE floats are such a good default. They balance those tradeoffs extremely well, with fixed semantics and near-universal hardware support.
I believe that only bigints among them are the must-have for practical programming languages, while rationals would be good to have but can be rather easily constructed from bigints anyway. Bigdecimals are harder than what people seem to believe and can't really be a general solution.