Top
Best
New

Posted by mayoff 10 hours ago

Turns are Better than Radians (2022)(www.computerenhance.com)
223 points | 107 commentspage 2
slwvx 8 hours ago|
Yes, the idea of a turn [1] is interesting. And maybe useful.

I have a different question: What would it take for a compiler to remove (elide) the multiply by pi + divide by pi that the author uses as an example? I guess one would not have to go as far as a Lean proof that two bits of code produce the same result?

[1] https://en.wikipedia.org/wiki/Turn_(angle)

eru 8 hours ago||
Well, they don't produce the same result in floating point math, I'm afraid.

So you'd need to teach your compiler about what your formulas mean and what context you are using them in. (Ie are you actually doing geometry, or is your AI coding agent just trying arbitrary activation functions for your neuronal net experiments and some of them happen to look like geometry?)

nomel 8 hours ago||
It's a mistake to care about equality of floating point numbers [1]. You must usually consider the lower bits of the number as random.

I assume you're saying something other than this though?

[1] https://en.wikipedia.org/wiki/Machine_epsilon

ainch 7 hours ago|||
I think the point is that, from a compiler's perspective, it's not obvious how much you should be allowed to optimise code at the cost of changing the outcomes of floating points maths - do you allow 1e-10, or 1e-6, or 1e-4 level changes? Does your compiler have to run some test calcs to bound the scale of the change introduced by rewriting fp maths? Some compilers will let you opt in to rewriting floating point maths, but that's opt in so users understand that their numeric outputs might change between optimisation levels.

For more, there's a good post on this kind of flag in Rust: https://pythonspeed.com/articles/faster-float-math-rust/

zarzavat 7 hours ago||||
This statement is a little too strong. It's a mistake to care about the equality of floating point numbers after subjecting them to irrational operations. On the other hand, the entire internet runs on the fact that doubles exactly represent the integers up to 2^53.
eru 7 hours ago|||
Huh, what? Floating point numbers have a standard, you know. They aren't non-deterministic YOLO numbers.

By default, the compiler has to stick to what the standard requires, and can't just say add arbitrary imprecision.

Your epsilon is what you get when you try to analyse floating point numbers as approximations of real numbers. But they also have an independent life as bit patterns, and the compiler can't just willy-nilly muck around with these bit patterns.

nomel 7 hours ago||
Please look at the link where context is clear here. Floating point has limited precision and the complete inability to exactly represent some numbers.

Example, this equality check is false:

0.1 + 0.2 == 0.3

Because the last bits of a floating point number, after any practical chain of operations, is practically random, do to errors from limited precision. Yes you can always know what the result will exactly be for any operation, if you know the exact number and operations used. Good luck with something like sin/cos though, where implementations can vary wildly depending on the platform/library.

eru 7 hours ago||
Your problem only occurs when you try to naively transport equality of real numbers into equality of floating point numbers.

https://www.netlib.org/fp/dtoa.c is how eg CPython parses literals like 0.3 into floating point numbers and how it converts floating point numbers back to strings. Lo and behold: these algorithm compare floating point numbers for equality, and would break catastrophically, if the compiler were allowed to willy-nilly fiddle with the bit patterns.

The authors of these algorithm did care about floating point equality, and that is not a mistake. (However it would be a mistake to assume that equality of mathematical real numbers translates to equality of floating point numbers.)

> Yes you can always know what the result will exactly be for any operation, if you know the exact number and operations used.

Yes, and for some algorithms like illustrated above this is exactly what people do, and have to do.

And the lower bits of 0.1 + 0.2 ain't random: they are the same on your computer as on mine, whether we run the code in 1999 or in 2029.

https://news.ycombinator.com/item?id=47767398 has a discussion.

jcranmer 7 hours ago||
The short answer is you need fast-math flags to allow optimizations that may change floating-point results, and you also need to guarantee an implementation of sinpi/cospi (these were added in C23, so they're not all that common in host library implementations yet).

It's possible if you had the implementation of the math library visible to the compiler that it could do inlining and then simplify expressions, but honestly most math library function implementations are going to be the kind of function that doesn't get picked up by inline heuristics, as there's a pile of if statements (handling special cases and range reduction) that the compiler can't eliminate due to there not really existing a sufficiently powerful FP range analysis.

amelius 1 hour ago||
The problem with this is that when I see pi I know we're talking about an angle; when you use turns it's just some number. Maybe in typed languages it would work better.
__MatrixMan__ 5 hours ago||
This seems to be mostly from the perspective of what makes the most sense to use at an API boundary.

Rather than trying to agree on the best meaning the various integers or floats that we're passing around, maybe we should instead build a more complex angle type that doesn't force callers to conform. Like, I can pass minutes or seconds to functions that accept a time type and it just works because they're not being collapsed to numbers. Is there any reason we couldn't do that with angles too?

kens 7 hours ago||
One weird unit for angles is the mil, defined as 6400 mils in a circle. This unit is very useful for artillery, since 1 meter displacement at a distance of 1 km is 1 mil [†]. Thus, you can see how much you missed by, divide by the distance, and easily determine how much you need to adjust your aim in mils. Another interesting thing about artillery is they traditionally do a binary search to get the distance correct, which they call "bracketing". Link: https://unitedtaskforce.net/training/sop/communication/artil...

[†] Note that this isn't exactly correct since it corresponds to pi = 3.2. A mil is almost the same as a milliradian, but 6400 mils in a circle is much more convenient than 6283.18... milliradians in a circle.

kqr 7 hours ago|
It's also useful for sighting distances when the width or height of something is known. A knuckle on your outstretched arm is roughly 30 mils, so you cover the thing with your hand, count knuckles, multiply by 30, then divide the size by that number to get the distance.

You can calibrate your knuckles by doing this is reverse. Put up a target 1 cm wide and back up until it's just covered by a knuckle. Measure how far you got and divide.

It was when I thought about why this works I started really understanding radians.

kqr 3 hours ago||
Oh, and I forgot and now it's too late to edit my comment. 6400 has a bunch of nice divisors too. A half-turn is 3200 mils, a quarter is 1600, a quarter of a quarter is 400, etc. A sixth of a turn is nearly 1000 mils. A tenth is obviously 640 mils.
em3rgent0rdr 7 hours ago||
And could use fixed-point decimal for more efficiency since can store as integers and use integer hardware for them. So for instance with 32-bits, the 16 most-sig bits store the number of turns and the 16 least-significant bits store the fraction of a turn. Then if you want to wrap angles that exceed 360 degrees back around the circle, you can simply Logical_AND with 0x0000FFFF. And while you are at it, you could just use fixed-point decimal for sine and cos, whereby the maximum of +1 or -1 map to the most positive and most negative integer value. These type of optimizations were common before FPUs were cheap and fast.
aldonius 6 hours ago|
Binary fractions of a turn are also a nice intuition pump for two's complement in general.

Let's keep it simple and use just 8 bits. 0° is 0x00, 180° is 0x80, and 255/256ths of 360° is 0xFF. And if we wanted to use signed integers, then 0x80 through 0xFF - the high-bit half of the range - now represent the negative quadrants just as they represent negative integers.

djmips 5 hours ago||
and that's exactly what we did in the old days of 8 bit games. We called them BRADs but others had their own names.
boomlinde 2 hours ago||
This can be useful for some geometry, but pi isn't a completely arbitrary choice and some useful relationships are lost when not using radians.

I use different angle units depending on the application. On a platform with 8-bit index registers, 1/256 of a turn can be useful. IIRC Pico-8 uses turns.

zahrevsky 8 hours ago||
> It turns out (pun intended!)

Thanks, I was waiting for this pun the moment turns were introduced in the article.

jp57 8 hours ago||
Or you could use 1/360 of a turn.
groundzeros2015 8 hours ago||
degrees were primarily chosen due to many integer divisors - likely for applications of time and seasons.
math-man 8 hours ago||
[dead]
fph 3 hours ago||
This alone should be a reason to drop the pi factor: it's basically impossible to get an exact zero for the sine of a half-turn:

sin(1*pi) = 1.2246e-16

rajnathani 6 hours ago|
Dumb question: For multiplying for smaller turns such as 1 arc-second (1,296,000 in 1 turn), that would floating point precision issues be a tiny slight issue (22619.4671 arc-seconds in 2pi radians), or is it just a coding convention change?
More comments...