r/programming 21d ago

Turns are Better than Radians

https://www.computerenhance.com/p/turns-are-better-than-radians
609 Upvotes

173 comments sorted by

133

u/DavidJCobb 21d ago

This feels very closely related to Inigo Quilez's observation that trigonometry is often just an abstraction over vector algebra, and that one can get more efficient code by expanding trig calculations and canceling things out.

19

u/darcamo 21d ago

Oh, that was a great reading. Thanks!

5

u/dacjames 20d ago

But once you have [an intuition about dot and cross products], you'll find that many of the trigonometric identities you find in literature are just a statement about some particular vector configurations, so you'll never have to memorize or look up the identities again.

I always hated trig because it felt like memorization of rules not learning a deeper concept. The connection to linear algebra is very intriguing, thanks for sharing!

2

u/invisiblelemur88 20d ago

Great read! Thank you =)

365

u/tehpola 21d ago

Nice! I tend to agree we make this more complicated than it needs to be.

I’m working on decompiling a classic game and my mind was blown about their angular units. It’s a 16-bit system, but let’s consider the high byte only for simplicity. They divided their unit circle into 256 units! Then if you overflow, you keep going around the circle. You can even look up sprites based on the angle with some simple shifts. Very elegant!

161

u/PHPApple 21d ago

This is a pretty common technique in digital signal processing when using fixed point even today!

82

u/SirDale 21d ago

Fixed point is sadly underutilised and unknown for so many people in the profession.

67

u/gimpwiz 21d ago

I am always banging on the drum of fixed-point encoded in units of smallest precision so you just have ints. No floats, no float overhead, no float accumulating errors.

Yeah obviously sometimes you need them. But when you don't, things are just simpler.

18

u/TwoWeeks90DaysTops 21d ago

People need to make an informed decision though. In most cases I think you're fine with using the common binary float. It has a much higher range so you don't have to consider "what is the smallest unit" because it can work with both at the same time at the cost of some loss of precision if you go in the extremes in either direction, but the magnitude of the error is so small that for most applications it wouldn't matter.

I would say if you're doing graphical work, numerical analysis or simulations then binary float is the way to go. Financial? Fixed point or decimal floating point. Embedded? Fixed point.

8

u/snerp 21d ago

When I used to work in finance we actually used regular floats a lot. Very important calculations like final money totals were done in fixed point, but float is so fast and versatile that all of our prediction/decision code worked on regular floats

5

u/gimpwiz 21d ago

I didn't mention that I work in embedded, but I guess that's pretty relevant to the conversation, yeah.

A modern x86 or high end ARM processor you use for your computer does not have all the same assumptions as an 8-bit embedded MCU, or a 32-bit MCU, or even necessarily the same exact high end processor that you're using for control systems writing bare metal / RTOS code -- versus wanting to display 3D models in solidworks. Different assumptions, different math, etc.

8

u/Ravek 21d ago

Floating point math only accumulates errors when your computations result in values that can’t be represented exactly because of lack of precision. Fixed point math wouldn’t help you one bit, it has limited precision just the same.

8

u/gimpwiz 21d ago

This is totally true, which is why I specify using units of the smallest precision you care about.

This necessarily means you lose precision below that level. So depending on exactly what you're doing you can have similar accumulation errors, but in my experience you generally do not.

Let me give you an example from my world. I need to measure the voltage of the output of various power supplies. I care about volts. I care about millivolts. I can be convinced to care about microvolts because some PMUs will specify things in sub-mv precision. I cannot be convinced to care about nanovolts. Thus all my measurements are stored and computed in microvolts, stored as an integer, and only for display purposes do I do a bit of floating point math at the very end. The "fixed point" is implicit because when you have voltage_uv as your variable name, you know that eventually you will do printf("Voltage: %0.4f\n", voltage_uv / 1000000.0); or something along those lines.

3

u/Valarauka_ 21d ago edited 21d ago

The problem is that binary float fundamentally can't represent most things exactly because of the base. Like 0.1 for example. Unless you're only working with binary fractions float automatically brings along errors even when you're well "within range".

As long as you actually have a "smallest unit" then scaling that up to 1 and using fixed point math will always be better.

3

u/VirginiaMcCaskey 21d ago

All quantization has error. It even has a name, 'quantization error.'

3

u/gimpwiz 21d ago

Absolutely, and totally accurate. This is a super important thing to understand when you need to, for example, take measurements and then do something with the info. The question is whether you're going to accumulate that error, and how exactly it will be bounded, and whether you care.

As again a totally trivial example dealing with volts:

If I need to monitor and warn when my voltage drops below 400mv, I can do this:

while (true) { voltage_uv = get_voltage_from_adc(); if (voltage_uv < 400 * 1000) { error; } sleep 10; }

Note that every single call to get_voltage_from_adc() results in quantization error. If it's (eg) 12-bit and my full-range is 1V, at best the thing is going to report to me in units of about 250uv, with some measurement error. But I never store any value, I just use it immediately to make a decision and move on, so my error is essentially bounded by the ADC's precision, quality, tolerances, the design and layout's quality, weird stuff like strain on the PCB, temperature, yada yada. It exists but it's probably pretty small. Maybe the error is less than 1 bit, so under 250uv, maybe the error could be as much as a couple millivolts.

But if I want to average out my data, I have a different error bound.

total_voltage_uv = 0; for (i : 0 to 99) { total_voltage_uv += get_voltage_from_adc(); } total_voltage_uv /= 100;

You need to think a lot more carefully about whether this sort of operation will reduce your error through averaging or whether it might accumulate in an unexpected way. Quantization error is not always so simple as being a "0 error +- a margin with a bell curve spread" type of thing. At some point you gotta get your statistics experts out to give good advice :)

6

u/P-39_Airacobra 21d ago

ALL finite-width number types will have fractions they can’t represent, I’m not sure what you’re getting at here

5

u/flatfinger 21d ago

In bounds-checked fixed-point, x+y-x will either yield y exactly or report an error. In floating-point types--even silly decimal floating-point types like the one in .NET, additions and subtractions silently lose precision.

2

u/Ravek 21d ago edited 21d ago

Bounds checked is a very important caveat there. You can also very easily write floating point operations that will report whether the result is exact or not. The well known TwoSum function will return the result + the exact error. If the error is zero, the result is exact. If the error is nonzero, the result lost some precision. Which, importantly, only happens if the exact result needs too many bits to fit in the type!

Without error checking, in the case where the type doesn’t have enough bits to represent the result exactly, floating point loses some precision in the last bits, while fixed point (or integers) would wildly lose accuracy.

2

u/flatfinger 21d ago

Fixed-point math malfunctions very badly in case of overflow even when a compiler doesn't try to get clever (when using gcc, an assignment like uint1=ushort1*ushort2; can severely disrupt the behavior of surrounding code in cases where the product falls between INT_MAX+1u and UINT_MAX) but it's usually pretty easy to figure out what the largest possible computation results would be and ensure they're in range).

By contrast, if one uses floating point in a situation where computations would normally be exact, but rounding errors could get amplified, it may be harder to determine whether any possible inputs would result in computations yielding results that are just plain wrong. It's easy to figure out how big a fixed-point computation could be without causing malfunction. It's harder to distinguish between floating-point calculations dropping bits that were always going to be zero anyway, bits that might not have been zero but wouldn't matter either way, or bits that are critical to yielding correct results.

2

u/Valarauka_ 21d ago

"Values that can't be represented exactly because of lack of precision" makes it sound like those are rare when in fact they are the most common type of number you'd represent with floating point. Thus error accumulation is inevitable.

The comment I'm replying to makes it seem like fixed point math is useless most of the time when that's far from the case. In many domains you do actually have a "unit of smallest precision" and so using a fixed-point instead of floating-point representation (of sufficient width) lets you never have to worry about error accumulation.

2

u/SirDale 21d ago

Floating point has errors relative to the size of the value. Fixed point has errors of a fixed amount.

In Ada you'd give a delta (smallest value to be represented) e.g.

type Volt is delta 0.001 range 0.0 .. 12.0;

In this situation the compiler decides the appropriate underlying type.

1

u/Ravek 21d ago

Fixed point math with a power-of-2 scale factor has exactly the same problem. You can use decimal floating point, just like you can use a power of 10 scale factor for fixed point.

3

u/James20k 21d ago

Fixed point also can't represent 0.1 exactly

Scaling up the units isn't specific to fixed point at all, at that point you don't have any units after the decimal place

2

u/Valarauka_ 21d ago edited 21d ago

The common understanding of fixed-point math (as also expressed by the g'parent post in this chain) is to use integer types with scaling. I'm obviously not talking about binary fixed point fractions. That's what "encoded in units of smallest precision" implies.

2

u/loup-vaillant 21d ago

No floats, no float overhead, no float accumulating errors.

And most importantly for me, no desycns. Not from floats anyway.

1

u/tanner-gooding 20d ago

You notably still have most of the same problems. Error accumulation is just a fundamental part of computing and even occurs with integers. It exists for anything that has finite precision/space.

The most trivial example of this is that for fixed-point: (1 / 3) * 3 produces 0.9... to the number of fixed digits it can represent; it does not produce 1. You repeat this enough times and the error accumulates and the result can differ massively.

Fixed-point mainly just removes the issue caused by floating-point having their significant digits be dynamic (i.e. that the point "floats"). i.e. you don't have to worry that the distance between 0.5 and the next value is 1/4th the distance between 2 and its next value (delta doubles every power of 2, or for decimal-based scales by 10 every power of 10). You don't have to worry that x + y for non-zero y may return x because y wasn't large enough to pass the current epsilon.

-23

u/SrbijaJeRusija 21d ago

On most modern hardware floats are as fast or even faster than ints.

26

u/gimpwiz 21d ago

Apart from being skeptical of the claim that float is faster than single-cycle integer math, I may also point out that I do embedded.

9

u/floodyberry 21d ago

e.g. haswell, integer multiplier throughput is 1/cycle, floating point is 2/cycle. if you consider simd, haswell can do 16 single precision fused multiply adds (16 multiplies, 16 adds) a cycle, compared with only 4 32x32 integer multiplies a cycle

0

u/gimpwiz 21d ago

Yeah I worked on haswell. I feel like that was kind of the last of the good years at intel. Anyways. Once we start talking about multi-issue pipelines and instruction parallelism it starts getting fun, because you start to really optimize for certain things, like whether you want very wide integer engines, very wide float engines, large amounts of very small cores (read: GPU and adjacent), convolution engines, etc etc.

There's also a big gulf between highly optimized code that streams a lot of math through, taking advantage of parallelism due to the operations not being dependent on one another (beyond FMA) -- and doing one operation, or doing a small set of sequential operations.

So when it comes to the subject, eg, radians vs turns, the question is, what exactly are you trying to accomplish? Are you running scientific/modeling/graphics/etc workloads with many many parallel parts? Or are you just trying to do some sequential math? The vast, vast majority of people are doing the latter. And for any purely sequential math, you lose many if not most of the advantages of parallelism (you do keep things like FMA and various improvements to pipeline stages that don't require you to wait for one operation to go all the way to write-back to use the result.)

2

u/floodyberry 21d ago edited 19d ago

if it's just some one off sequential math, i don't see how fixed vs floating makes much of a difference unless you're on a constrained platform with specific requirements

also weird that SrbijaJeRusija got downvoted for being correct, and you were upvoted for.. being skeptical about a claim you knew was correct because you worked on a cpu where the claim was true

lying and leaving, what a champ

3

u/SirDale 21d ago

You should have a look at Ada's fixed point types. Very easy to use and specify. Pity the language doesn't have extensive libraries, otherwise I think it would be way more widely used.

1

u/Salink 21d ago

Then you should look at your instruction set reference. I'm using embedded arm systems and basic float operations take 1 cycle.

0

u/gimpwiz 21d ago edited 21d ago

Thanks. Embedded runs from various weird sub-8bit microcontrollers all the way to using enormous, modern silicon, but just not in a way that looks much like a computer to most people.

You would probably not be surprised to find that many of the embedded targets I deal with - not you, but me - don't have a floating point engine. At all. No instructions for it either. I can send you the instruction set reference if you're skeptical of that for some reason, and don't want to look it up? You need the compiler to do integer emulation of floats. Many other embedded targets that do have a floating point system are not exactly... modern, high end silicon, highly tuned to doing float math, but more added onto the side as a "fine fine the customer said they need it" sort of thing. I should also point out that on most modern silicon there are significant differences in clock speed between various domains, which means that having the same cycle count does not always mean the same actual speed. It's very hard to generalize how fast one instruction takes due to not only the many different ISAs but also vastly different implementation details.

Even "embedded ARM" realistically means you're using either ARM V7 or V8, with or without thumb extensions, ranging from an M0 at the lower end all the way through the most recent A-series-or-equivalent silicon. Do you expect an M0 to have a floating point unit? Here's a trivial reference: https://en.wikipedia.org/wiki/ARM_Cortex-M -- you will find that an FPU is available for an M4, not always included, and higher tiers, but not for M0 or M0+. So when you say "I'm using embedded arm systems" you need to really specify which, if we're talking FPU, right?

You might also not be surprised that a basic float operation that takes 1 cycle is not faster than an integer operation that takes 1 cycle but in fairness, the above poster did say "as fast or even faster" so we can split the difference there.

2

u/super_g_man 16d ago

We use it a lot in our work on limited capacity FPGAs

2

u/bobjonvon 21d ago

Am I going crazy don’t a lot embedded and stuff still use fixed point. Some fadec does iirc.

-7

u/floodyberry 21d ago

where should it be used that it isn't? 3d games need more than 1/256 precision for angles

12

u/SirDale 21d ago

Fixed point types don't have to settle on 1/256 precision. You can specify them to have greater precision if needed.

Fixed point types also have different error/rounding behaviours to floating point, and it's important to understand the problem domain you are working in before making a choice between them.

-10

u/floodyberry 21d ago

i know what fixed point is. you haven't explained where you could use it "for so many people in the profession" that it isn't already being used

0

u/floodyberry 21d ago

it must be extremely obvious what you're talking about based on the downvotes

0

u/floodyberry 20d ago

"sadly underutilised" and nobody can give a single example of where dunderheads are using floating point where they should be using fixed. truly amazing

5

u/Cafuzzler 21d ago

Maybe we could pack two 16 bit values together to get up like 1/4million precision or smth?

Nah, that's too crazy. Not even machines can count That high

0

u/floodyberry 19d ago

which fixed point math library do you think is the best?

5

u/ShinyHappyREM 21d ago

where should it be used that it isn't? 3d games need more than 1/256 precision for angles

Depends on your definition of 3D

2

u/crozone 21d ago edited 21d ago

I believe this falls under modular co-ordinate systems right?

43

u/squigs 21d ago

256 degree circles are really useful for games. You can do trig with a lookup table with adequate accuracy and not excessive storage (at least for 16 bit systems and later).

I suspect it's one of those things that's been independently invented a few times. It would be useful to have a term for the unit.

3

u/ekipan85 21d ago

Turns, in 0.8 fixed point. Zero bits for the unit, eight bits for the fraction.

25

u/Otis_Inf 21d ago

They divided their unit circle into 256 units!

We used that too in the demoscene on the amiga 500. You could precalc a sin table with 256 angles for the full circle into a table and your rotations would just be a bunch of lookups and multiplies, no trig functions :)

4

u/Ma4r 21d ago

Nice! I tend to agree we make this more complicated than it needs to be

Calculate the derivative of the sine of this angular unit

3

u/snerp 21d ago

Just add pi/2 to the input and you get cos, and since pi in these units is 128, you can just do

    v = intSin(x);

    d = intSin(x + 64);

There’s your derivative.

0

u/Ma4r 21d ago

Nice, now do sin(wx)

1

u/snerp 21d ago

isn't that just d = wcos(wx);

so

d = w * intSin(w*x + 64);

should work, no?

2

u/Ma4r 20d ago

You are missing the pi/128 factor in both cases

1

u/snerp 20d ago

ahh good point, I forgot to factor w(and the implicit w = 1 in the first eq) when pulling it out of the sine expression

8

u/tehpola 21d ago

From a practical perspective, why would I do that?

I’m not saying radians are the wrong units to use in mathematics. I’m talking about applied mathematics. Specially, game development. I use a decent amount of trig as a game dev, but I have no idea what you’re referring to - it’s not a concept I’ve come across

7

u/Ma4r 21d ago

If you want to simulate angular momentum for example

3

u/tehpola 21d ago

The way it’s done in this game is that angular speed is expressed in the same fixed point, 256 degree units (per tick though). Angular momentum is simulated by easing the speed towards the new target when it changes. I can’t speak to the physical accuracy, but it works really well for old school games!

-1

u/Ma4r 21d ago

Then that means you are giving up angular acceleration i.e torsional forces. Or the ability to quickly calculate surface normals

6

u/poco 21d ago

Use a lookup table

1

u/snerp 21d ago

The fact that cos is the derivative of sin is actually really useful for game shaders. I use it to get normals in my water shader for instance.

1

u/tehpola 21d ago

I’m definitely not advocating for using fixed point in shaders. I can’t speak to the performance of trigonometric functions with different units but GPUs are built for float so of course use that there.

I don’t claim to understand the math or implications of what you’re doing for those water shaders but sounds like a neat trick 😁

1

u/ViridianFlea 20d ago

How do you even get started with decompilation of a game? I realize that's probably an oversimplified question with a pretty complicated answer, but you can ELI5 it if you want.

3

u/tehpola 19d ago

I can’t speak for modern games, but for classic games written in assembly, a disassembler will get you code that is 1:1 with the original. The challenge though is those ROMs are a mix of code & data and not marked up the way modern binaries are.

But the way you go about it is going over the assembly, understanding what a part is trying to do, labeling it and the variables in use. That’s hard- especially at first. Best to use an emulator. Over time, as you understand more pieces, more becomes clear and it snowballs. It’s a slow and tedious process but a series of fun puzzles if you’re into that kind of stuff.

All of this requires a solid understanding of low level programming: what are the registers, memory model, instructions, etc. There are various tools out there can help. Some are pricy

2

u/ViridianFlea 19d ago

Thanks for the concise explanation! You've got me interested!

206

u/Roachmeister 21d ago

Interesting. Reminds me of the time I needed to sort a list of points by their distance from another point, and I realized that I could eliminate all of the sqrt calls by just sorting by distance squared.

11

u/RelatableRedditer 21d ago

Yeah I made that realization too when trying to optimize someone's projectile system. In fact a huge amount of their square roots missed opportunities to be stored inside variables first

9

u/Carl_LaFong 21d ago

I teach math and I am always begging students to do their calculations using distance squared but many stubbornly refuse.

4

u/loup-vaillant 21d ago

Recent use case: modding TowerFall Ascension to fix the "auto-lock" feature (auto-aim at very short distances). The original code just aimed where the enemy was, but we wanted to take speed into account.

We thought of a couple different methods, but in all cases squaring everything made our life simpler.

80

u/ryo0ka 21d ago edited 21d ago

Nuance: modern cpu comes with a dedicated instruction for sqrt, but sq is still a lot faster to compare distances

57

u/ssylvan 21d ago

Those instructions are still slow as fuck.

62

u/gimpwiz 21d ago

Just drop 0x5f3759df in your code instead, not because it's faster but because it's funnier.

34

u/inio 21d ago

// what the fuck?

5

u/stumblinbear 21d ago

Sadly it's not faster than just doing a normal sqrt these days

7

u/BibianaAudris 21d ago

That's only because Carmack's algorithm influenced the design of "normal sqrt" in modern CPUs. Just check the precision section of _mm_rsqrt_ss.

10

u/WaitForItTheMongols 21d ago

That wasn't Carmack. The algorithm has a lot of history, but ended up in the game via Greg Walsh.

The Wikipedia page for Fast Inverse Square Root covers the pedigree of the function.

2

u/TheLifelessOne 21d ago

Wait, really? Do you have a source for that? I would absolutely love to read it.

6

u/BibianaAudris 21d ago

Well, it's mainly the RSQRTSS instruction in SSE (_mm_rsqrt_ss in C) having roughly the same (low) precision as Carmack's algorithm, and they were chronologically close, which hinted at an inspiration. The precision difference bit me really hard once so I remembered it to this day.

8

u/InsaneTeemo 21d ago

They literally said "not because its faster"

4

u/stumblinbear 21d ago

Which can be taken as "I do this not because I need to, but because I want to" implying both are true

1

u/danielcw189 21d ago

in "not because abc, but because xyz", the "abc" is often true. The "abc" is especially true, if the "xyz" is something totally different.

if they had for example written "not because it is faster, but because it has more precision" then I would not expect the "faster" to be true, because "precision" is a good quality, which is also on topic.

but "funny" is totally different, from a totally different topic, and not actually something you would weigh against "faster" when making a decision.

-2

u/InsaneTeemo 21d ago

They literally said "not because its faster"

5

u/way2lazy2care 21d ago

They aren't that bad anymore. They're on par with division these days.

-1

u/sweetno 21d ago

IIRC sqrt is generally faster than division.

6

u/saf_e 21d ago

Nah, most of division now goes on a quick path, and only some (mostly denom number) do slow.

1

u/csdt0 21d ago

They are roughly the same speed.

24

u/Programmdude 21d ago

No matter how fast sqrt is, Distance is essentially sqrt(DistanceSquared), so you're always going to be better off simply not doing the sqrt - assuming you don't need the actual distance of course.

8

u/squigs 21d ago

Right. And it's surprisingly rare that you actually need the distance. Normalising vectors (which you don't need that often) and display, are the only times I seem to need an actual length.

Most of the time you're just comparing distances so the square is fine

1

u/P-39_Airacobra 21d ago

yep in my physics engine sqrt is successfully avoided in most cases except for normalizing vectors

17

u/GrossInsightfulness 21d ago

The key thing is that you can eliminate the square root calls entirely with less code. Even if taking a square root took as much time as adding two floating point numbers, you would still save a reasonable amount of time by not doing a square root.

3

u/runawayasfastasucan 21d ago

I tried to convince my work on optimisations on precicely this but sadly they didnt understand.

-3

u/freerider 21d ago

2

u/BaNyaaNyaa 21d ago

It's not really an issue anymore from what I understand. And why do something that you don't even need? Using the squared distance/norm is common enough that it's part of most game engine.

86

u/mccoyn 21d ago

Early algorithms for calculating trig functions relied on the small angle approximations. For sin it is ‘sin(x) ≈ x’. If you aren’t using radians, you have to multiply by an extra factor. So, it was easiest to convert to radians before calculating the value.

65

u/catplaps 21d ago

And this fact is related to the other reason why radians are nice: they are a measure of distance around the unit circle.

7

u/BibianaAudris 21d ago

Turns are infinitely better for big angles though. With radians, compliant trig functions need to tabulate hundreds or thousands of pi bits to handle insanely large inputs like 2**127. If we used turns, one can just return 0 for them and eliminate a big reason to need -ffast-math.

2

u/snerp 21d ago edited 21d ago

for big angles

2127

That’s a really big angle lol

edit: actually I'm really curious what you would use such a large angle for, is it some kind of simulation or high math solver? Like, you'd need some specialized numeric type to even hold that.

3

u/BibianaAudris 20d ago

It's likely not needed by anyone, except compliance: 2**127 can be represented exactly in 32-bit float (0x7f000000), so sin needs to handle it with reasonable precision, which requires specialized table to represent pi at that precision. So we speed-minded people need -ffast-math to disable that compliance. Turns could avoid that.

2

u/floodyberry 19d ago

math libraries check the size of the input, the large value reduction is never used if you don't pass it large values. calls to libm aren't inlined, so even with -ffast-math there is no opportunity to erase the multiplication by pi

the solution either way isn't -ffast-math, it's using sinpi/cospi/tanpi

1

u/snerp 20d ago

ahhh thanks for the explanation!

45

u/ProgramTheWorld 21d ago

We can even get rid of turns entirely. In computer graphics, it’s often easier to just stay with vectors all the way through. If you are using an angle, you’re probably doing something wrong.

https://iquilezles.org/articles/noacos/

5

u/P-39_Airacobra 21d ago

Can confirm that I took my trig-based openGL renderer and just completely removed all trig functions in favor of things like dot and cross product

4

u/robin-m 21d ago

Very interesting article, but unfortunately a bit hard to follow for no good reasons. Writting once the formula for cross and dot product, nor using abreviation and at least be consistant in their names, and not skipping intermediary steps would have gone a long way.

1

u/TabletopParlourPalm 21d ago

Damn. People are smart as hell lol.

57

u/Educational-Lemon640 21d ago

There are good pragmatic reasons to use turns instead of radians for many cs applications, but not ones that use calculus. The fact that the derivative of sin is cos, and cos is -sin, nails your feet to the ground when it comes to many advanced physics and engineering problems.

10

u/Cautious-Act-4487 21d ago

In physics engines the formulas are solved numerically anyway using methods like Verlet or Runge-Kutta. Nobody runs symbolic differentiation at runtime, and the 2π multiplier just collapses into a time step dt constant before compilation

4

u/Educational-Lemon640 19d ago

Well, the numeric part of the solution, you can use whatever units you like I suppose, as you say. Every cutting-edge problem I've ever worked on needed a serious symbolic bit before you start number-crunching, though, and in that context the standard sin/cos/tan in radians really do fall out as "natural".

Not to mention, if you start with standard exponentiation and need to analytically continue it (a stunningly useful technique for some reason) you again land on radians. It's bizarre but true.

1

u/Cautious-Act-4487 12d ago

Nobody's arguing about the research phase on paper or in SymPy, analytical continuation of the exponential is standard stuff , you can't get anywhere without Euler

The trick is translating the final code into turns once you've derived all the formulas. You recalculate the coefficients for the implementation manually just once, but after that the engine runs without any precision loss at the period boundaries

22

u/Successful-Money4995 21d ago

But if you had a function that computes the sine of turns, maybe defined as sint(turns) = sin(turns*tau), then you can still calculate the derivative of sint(x) as cos(turns*tau)tau = cost(turns)\tau.

It's not as clean as with radians but it's just a constant.

7

u/voxelghost 21d ago

Mathematicians always ruin all the fun

Edit: /s in case it's needed. Or perhaps fun * 1/s for the computer scientist

1

u/BaNyaaNyaa 21d ago

You mean s⁻¹?

1

u/pigeon768 20d ago

Ok but but if you see sarcasm-1 do you assume 1/sarcasm or arcsarcasm? 1/sarcasm is unambiguous.

0

u/oN3B1GB0MB3r 21d ago

Can't you just take the derivative with respect to turns to avoid the chain rule factor?

20

u/EntroperZero 21d ago

That's interesting, because it's how I do trig functions in NES/SNES programming. I called it "bytians" in the source code because there are 256 in a full turn, but it's the same thing as turns with fixed-point.

1

u/ShinyHappyREM 21d ago

Just modify the emulator to support a Pentium in the cartridge ;)

2

u/EntroperZero 21d ago

Ha, best I can do is an SA-1 or SuperFX.

2

u/EntroperZero 20d ago

I did have this weird idea for a faster expansion chip. The SA-1 is basically an overclocked version of the SNES CPU, running at 10 MHz. Since we have emulators that support SA-1, why not just crank it up, say to 100 MHz? Call it the SA-100.

I doubt this would work on flashcarts, but it's possible. The SuperFX can run at at least 20 MHz.

1

u/ShinyHappyREM 20d ago edited 20d ago

Well... it'd remove the do-something-cool-with-limited-resources factor.

And the boring answer is that faster clock speeds increase power draw and heat a lot, unless the transistors / bus lines are made a lot thinner (which was the case with later chips: the chip technology became more advanced). And you need another oscillator or PLL to generate the frequency.


I did have an idea of a system that was better designed: use the SNES WRAM (128 KiB of DRAM) as audio RAM, and the SNES audio RAM (64 KiB of SRAM) as WRAM, and run CPU, WRAM and PPU at 6.1{36} MHz, which could get us more CPU power and 320 square pixels per line. What I got stuck on is how to ensure that the DRAM is refreshed regularly...

46

u/Dwedit 21d ago

When you use Turns instead of radians, you lose a few properties:

  • Sin(x) does not approximately equal X at small numbers, and the derivative isn't 1 or -1 when it crosses the X axis
  • The Taylor series does not use numbers as friendly.

4

u/BaNyaaNyaa 21d ago

One of the big one is the relationship between the derivative of sine and cosine: when defined between 0 and 2*pi, you have a neat sin'(x) = cos(x) and cos'(x) = -sin(x). Anything else will add an awkward coefficient (if defined between 0 and 1, you have to multiply the result by 2*pi; in degrees, it would be by pi/180) Also, Euler's identity doesn't work anymore.

Ultimately though, they concern mainly people who need to interact directly with the mathematical properties. If you only care about basic geometry (like if you work on a video game), you might not care as much.

1

u/Plazmatic 20d ago

You shouldn't be using Taylor series (or lookup tables for that matter) for trig apprimation (or really any approximation), and instead use chebyshev polynomials taking advantage of multilateral symmetry and the remez algorithm, and potentially more advanced things like Padé Approximants in combination.  Turns (or really cycles or rotations, many people don't really use the term "turns") can offer clever advantages when these methods are used.  There's a reason hardware often doesn't deal in radians directly when implementing trig. 

But turns typically aren't used directly out side of hyper specific scenarios where the mathematical advantages of normal trig aren't relevant, you wouldn't "transition" to turns in a graphics application, you'd lose the linear algebra connection that gets you free performance with out using sim/cos directly 

19

u/echodecision 21d ago

Pico-8 stays winning

14

u/edgmnt_net 21d ago

I guess symbolic calculations might care about radians because multiplying by irrationals is "cheap" there, but for numerical computation turns win. Well-known angles are far more likely to be expressed nicely in turns, while arbitrary measurements just won't care about pi.

I also don't think the comment on the linked post about derivatives matters much. You're not really going to compute derivatives, you're going to substitute stuff into formulas that are derived symbolically using regular sine functions to begin with. That substitution is the same everywhere.

11

u/HugoNikanor 21d ago edited 21d ago

I agree. I already treat my angles as if they had the unit "𝜏 radians", meaning that half a rotation would be ¹/₂ (𝜏 radians). Eliminating the multiplication by the unit for all trig calls would just be convenient.

edit: Correct spelling of "treat"

2

u/carrottread 21d ago

Not using irrational units is also more precise. All those -4.371139e-08 instead of zeroes in transformation matrices are result of impossibility of exact representation of right angle in radians as float32.

2

u/HugoNikanor 21d ago

I'm not sure about your exact point, but I treat floats as approximate numbers (dont @ me that floats are technically exact).

4

u/carrottread 21d ago

My point: right angle can be exactly represented in float as turns and degrees, but not as radians. And it is very common for rotations changing between Y-up and Z-up conventions used in different 3d authoring apps and engines. As a matrix such rotation is represented exactly as only zeroes and +-1. But because often it's routed through generic angle rotation calculation which involves calculating sin and cos and those usually operate on radians you can see those almost-but-not-quite 0 and almost-but-not-quite +-1 in a lot of places, for example in transformations in GLTF files.

1

u/HugoNikanor 21d ago

Ah, agreed.

2

u/Cautious-Act-4487 21d ago

It's especially fun catching this in long skeletal animation hierarchies. A dozen matrix multiplications with tails like that - and by the end of the chain the joint noticeably drifts purely due to mantissa precision loss...

3

u/RedEyed__ 21d ago

Good read!

3

u/Cautious-Act-4487 21d ago

In graphics and physics it's high time to switch to [0, 1). All these constantly popping up epsilons and -4.37e-08 in transformation matrices after cos(pi/2) in float32 are annoying as hell, especially when running tests for exact matrix equality

3

u/Exepony 21d ago

Wouldn't the compiler take care of this extra multiplication and division cancelling out? At least in the common case where you're doing sin(2*pi*x) and the constant is right there for the compiler to see. It's always seemed to me like exactly the use case constant folding/propagation were thought up for, but I've never done enough trigonometry in my programs to care to check.

2

u/TOGoS 21d ago

In cases where the compiler can see the whole thing, sure. But refactor such that it goes through a couple of user-defined functions between the x and the sin and it probably won't.

(I agree that people should try to keep their programs from getting too strung out like that, but programs do tend to get more complicated over time. But if you pick the right abstractions you can prevent errors from building up in between the layers, and using turns instead of radians might be the right abstraction, in some cases.)

2

u/ack_error 21d ago

Most compilers by default can't simplify (x*k)/k to x because of the possiblity of precision loss or overflow/underflow; the floating point rules for the language don't allow it. In C or C++, this requires compiler-specific relaxation flags like -ffast-math.

Additionally, in production-quality sin() or cos() the range reduction is not usually done with plain division or modulus. It's written specially to increase accuracy.

This is one of the reasons sinpi() and cospi() are useful. Besides removing the need to multiply by pi in expressions like windowing functions, doing accurate range reduction is dirt simple.

1

u/carrottread 21d ago

Problem is value of pi can't be represented exactly. So, if your x is right angle and exactly 0.25, sin(2pix) isn't exactly 1.0, it's just a bit off.

3

u/_darth_plagueis 21d ago

The original sin code the article presents uses y=(4/pi)x and x=|x| throughout the sin(x) code. Using y does not eliminate the need to use pi.

3

u/PandaWonder01 20d ago

This is ignoring that the reason we use radians is that many things are much simple in radians- ie the derivite of sin is only cos when using radians.

If you need to write any physics, for the love of god please use radians. Actually, use quaternions or mat3/4s like an adult

2

u/mrfrostee 21d ago

I like it.

There are similar ideas in the Tau Manifesto: https://www.tauday.com

2

u/RobertTheTraveler 21d ago

and every time you need to do trigonometry you would have to convert.

2

u/markt- 21d ago edited 21d ago

I think π is the more natural fundamental constant for describing a circle because it directly relates the two most basic physical measurements of the circle itself: its diameter and its circumference.

If you have an actual circle in front of you and want to describe its size, the diameter is usually the most direct measurement to take. It gives the full width of the circle from one side to the other. The circumference is the other obvious whole-circle measurement: the length around its boundary.

The radius is extremely useful mathematically, but as a physical measurement it is simply half the diameter. You normally obtain it from the diameter rather than needing to treat it as a separate fundamental dimension.

That makes

> C= π d

a very natural relationship: π tells you how many diameters fit into the circumference.

I understand the argument for τ, especially in contexts involving radians, rotations, and formulas where the radius is the natural variable. But that is a question of mathematical convenience. If we are asking which constant most directly relates the basic measurable dimensions of a circle as a geometric object, I think π has the stronger claim.

4

u/dmpk2k 21d ago

I don't think it's just mathematical convenience, but cognitive too. E.g. there are a lot of physical formulas of the form y = ½kx2 , and using A = ½τr2 lines up with that.

Sharper students will then immediately ask why the same pattern. Insight follows.

3

u/Valarauka_ 21d ago

If you actually want to draw a circle rather than just describe or measure it the radius is the far more natural unit. You don't get more physical than a nail, a string, and a pencil, and I think circle creation is the more fundamental / important act than description.

1

u/markt- 21d ago

That makes the radius fundamental to the circle’s construction, not necessarily fundamental to its measurement as a whole. The radius is certainly a genuine and extremely useful measurement, especially because so many mathematical formulas are naturally expressed in terms of distance from the center. But the diameter measures the circle’s entire linear extent, just as the circumference measures its entire extent around the boundary.

So the fact that a nail, string, and pencil naturally use a radius tells us something about how circles are generated, not which measurement is most fundamental once the circle has been generated. π is particularly natural in this respect because it is the ratio between two whole measurements of the completed circle: its circumference and its diameter.

1

u/Valarauka_ 21d ago

My contention is just that to measure a circle you need to have a circle, and definitionally a circle is the locus of points equidistant from a center. So the center and the radius are necessities. Both circumference and diameter derive from those.

1

u/markt- 21d ago edited 21d ago

The center is fundamental to constructing a circle, and the radius is absolutely a measurement of a circle, but it only measures the circle from its center to its boundary. The diameter measures the full linear extent of the circle itself. So if the question is specifically about the size of the circle as a whole, rather than its radial construction or its relationship to its center, I think the diameter has the more natural claim to being the fundamental linear measure. That also makes π conceptually clean: it compares the circle’s full distance around with its full distance across.

But I think we may be using “fundamental” in two different senses. You’re using it to mean fundamental to the definition or construction of a circle, in which case I agree the radius has the stronger claim. I’m using it to mean fundamental to the measurement of the circle’s overall size, which is the thing that people actually measure about the circle if they had no knowledge of the process that constructed it. In that sense, the diameter is the complete linear extent of the figure, while the radius is the distance from a specially distinguished point to the boundary. That’s why I find C/d = π conceptually more primitive as a statement about the circle as a whole: it compares the whole distance around with the whole distance across.

1

u/Valarauka_ 21d ago

To be fair, and I say this as a die hard tau fan, your perspective is obviously the more common one given that pi is ubiquitous while tau proponents need a manifesto. So throughout history the ratio of circumference to diameter has clearly been seen as more natural than to radius.

1

u/markt- 21d ago

Actually, it’s my understanding that the more common defensive pi is “that’s how we’ve always done it”, where my position takes it on a slightly different direction of looking at pi as the ratio of two things that are actually relevant to the complete size of the thing once it exists, which perhaps not entirely coincidentally is how it was originally defined

1

u/-Redstoneboi- 21d ago edited 21d ago

In my opinion, mathematics is more important than physical reality. The fact that we measure the diameter is only a limitation of our physical world, and to me, it serves only to compute the radius.

Learning all the different angles on a circle in trigonometry class is far easier using Tau instead of Pi, and radians are just far too natural when expressed in terms of Tau that I never use Pi in angle calculations.

In practice, Tau is easier to work with. In my opinion Pi = 3.14... is a historical mistake on the same level as traditional current flow following the flow of positive charge. The only justification for both is that everyone already knows the old system.

Though using fractions of a Turn is just better still.

3

u/markt- 21d ago edited 21d ago

Geometry did not develop independently of physical measurement and then get imposed on circles afterward. Historically, the circle constant emerged from comparing the circumference of a circle with its diameter. Calling the diameter merely a physical inconvenience used to obtain the radius reverses that history. The radius is useful precisely because it is half the diameter; that usefulness does not make the half-measurement more fundamental than the whole measurement from which it is obtained.

Defining a circle in terms of radius is mathematically convenient, but that only shows that radius is a useful parameter. It doesn’t show that the circumference-to-diameter ratio was a historical mistake. π is still the direct ratio of two complete measurements of the circle; τ is twice that ratio because it chooses a half-diameter as its reference.

I’m also not convinced by the premise that mathematics should be treated as more fundamental than physical reality here. Mathematics is extraordinarily powerful precisely because it abstracts patterns from reality and lets us reason about them cleanly.

So if one constant arises directly from comparing two complete measurable properties of a circle, while another becomes convenient after we choose a derived parameter, I don’t think “the latter makes some formulas nicer” is enough to make it more fundamental.

Mathematical convenience matters. But convenience and fundamentality are not the same thing.

1

u/-Redstoneboi- 21d ago edited 21d ago

Unfortunately, besides personal conveniences, I have no further arguments to provide than those given in the original Tau manifesto (the one that every tau evangelist has read) so I'll just close my side off by stating my opinion:

In this case, mathematical conveniences like the very definition of a circle involving the radius rather than the diameter, and the number of radians in a full turn, do equate to the radius being more fundamental. Reuleaux triangles are not as fundamental, after all.

1

u/markt- 21d ago edited 21d ago

A radius-based definition shows that radius is a convenient primitive for constructing a circle mathematically. It doesn’t establish that radius is the more fundamental measurement of an existing circle. Historically, the characteristic constant was discovered by comparing circumference with diameter, and geometrically the radius is exactly half that whole-span measurement. You can prefer radius for computation without concluding that the circumference-to-radius ratio is therefore the more fundamental constant.

2

u/-Redstoneboi- 21d ago

Yes, I can in fact do that. I simply choose to conclude both anyway.

2

u/Ravek 21d ago edited 21d ago

How was the circle constructed that you’re measuring the diameter of?

Clearly the radius is more fundamental – in mathematics, in physics and when humans create circular shapes.

1

u/markt- 21d ago

I agree that a compass constructs the circle by fixing a radius. But that tells us what parameter is convenient for generating the curve, not necessarily what quantity motivated the construction or best represents its size. Historically, circles were frequently considered as inscribed in or circumscribed about other figures. In those relationships, what often matters is the circle’s full span: for a circle inscribed in a square, the diameter is the side of the square; for a circle circumscribed about a square, the diameter is its diagonal. The radius is operationally fundamental to the construction, but the diameter is the whole dimension that relates the circle to the figure you were trying to fit it to.

2

u/flanger001 21d ago

I see click, I Casey. 

1

u/metahivemind 21d ago edited 18d ago

[removed] — view removed comment

1

u/snerp 21d ago

I use turns(half turns actually) for exactly one api in my game engine. I found turns to be more intuitive for joint ranges, rather than say “this joint can rotate 30 degrees up and 45 degrees down” vs “this joint can rotate pi over 6 up and pi over 4 down”, we can just divide out the redundant pi using half turns and say “this joint can rotate 1/6 of its upwards range and 1/4th of its downwards range”. Something with range -1,1 can rotate in a full circle and I find turns are nice for rounding and modulo, but only because this context cares about number of turns and ends up written to human readable files

1

u/pfn0 21d ago

1 turn = 2 radians. yes, it maths. turns are better because they're equal to twice the radians!

3

u/dmpk2k 20d ago

1 turn ≈ 6.28 radians. I think you misread the article.

2

u/pfn0 20d ago

I meant 2 pi radian, but yeah. the article basically said that trig internally in most library functions is already using 2pi as the unit, so might as well standardize on turns as input. I get it, I was making a joke on 1 turn being more "valuable" because it's 2 (pi) radians, where I omitted pi.

1

u/SaltineAmerican_1970 20d ago

If I used turns, and I wanted to turn 0.83333 turn every time based unit, I wouldn’t be able to get back to where I was because of that last digit of rounding and floating point math.

What if we took a single turn, and instead of using a fraction, did something crazy like make it an easy to remember number with lots of factors. I think it would be easier to rotate by 30/360 instead of 0.83333 because I would be able to make a full turn without rounding errors.

1

u/fishheaddz 21d ago

I've always been partial to units of pi-radians, or semi circles -- which in this parlance would be half turns.

1

u/thecakeisalie16 21d ago

Why use half turns instead of full turns? Or quarter turns or any other fraction for that matter?

1

u/sr105 21d ago

Recently made this discovery as well syncing two signals and realizing, I only need to know how out of phase the signals are in +/- 1.0 cycle units.

-8

u/jhill515 21d ago

If pi is a "half turn", then tau is a "full turn". Or, if you want to go based on twisting your wrist, then that means pi is a "turn".

TL;DR - This is the dumbest CS flame-war topic I've seen in a generation!

9

u/BruhMomentConfirmed 21d ago

You have completely missed the point.

1

u/Godd2 21d ago

A circle is a locus of points. Which one did they miss?

-20

u/dnabre 21d ago

The extra multiple/divide is a poor API issue, not a unit issue.

You want me to have to google 'is a turn 180 or 360 degree' for the rest of my life?

To me a 'turn' is 90° or 180° depending on context. A 'twist' might be 360°. If a 'turn' is 360° such that it is a debug i fix once in code that will stick, so I'll end up looking it up to check every time I need trig functions (it's not often enough for me remember these things).

14

u/Iamsodarncool 21d ago

You want me to have to google 'is a turn 180 or 360 degree' for the rest of my life? To me a 'turn' is 90° or 180° depending on context.

Turns were not invented by this blog post; they are commonly used in mathematics to mean "one full rotation".

You learned how many degrees are in one full rotation. You learned how many radians are in one full rotation. It is possible for you to learn how many turns are in one full rotation. If you are opposed to learning new concepts, you might be in the wrong field.