Can anyone explain these errors when adding/subtracting voltages

I’m using offset voltages and sample and hold modules to manually generate CVs to address a sequencer, by adding or subtracting 1/12V to the previous voltage. Setting aside the reasoning for doing so, I’m encountering an issue where adding and subtracting the same voltage (0.083333V) the same number of times is not resulting in 0v, e.g. + + + - - - - - + + =/= 0

Is there a universal limit on precision, or can it vary by module? It feels like a rounding error, but I can’t identify the source.

Thanks in advance!

Most of the math in VCV Rack is done using 32-bit IEEE floating point, so yes there can be cumulative roundoff errors, especially because 1/12 is not a fraction that can be expressed perfectly in binary. The error should not be very large however. Each floating point number should have roughly 6 decimal places of accuracy. What size errors are you seeing?

1 Like

Enough that it’s introducing errors, i.e. skipping or repeating steps of the sequencer after a fairly short period of time.

I think part of the issue may be because I’m addressing the minimum voltage, rather than the centre of the range, so it only takes a small error to throw it off; I’ll try tweaking on that basis and see if it solves it enough in practice.

Thanks for the help!

1 Like

Rading about skipping and repeating steps, I’m thinkin perhaps you’re not considering the cable delays ?

I suggest posting a patch or a screenshot.

1 Like

Great! Using the center of a range sounds like it will work great since you will have a huge buffer around any floating point inaccuracy. Another possibility is to use an increment that can be represented exactly by 32-bit float, like 1 volt. If you can add/subtract 1V, and do your control logic there, and divide by 12 at the end of the chain to drive quantizers (or whatever you are doing), that would always come back exactly to 0V.

I also second what @Jens.Peter.Nielsen said about cable delays. It’s possible that limited floating point precision isn’t the actual problem or not the only problem.

1 Like

That’s not true, actually. VCV and plugins use -funsafe-math-optimizations, like most DSP code. With this option the accuracy is slightly worse than IEEE. Also there are runtime flags that make the math in VCV deviate from accurate (all for the good, of course).

All these options make the FPU deviate from IEEE specs, but are mostly good for what we do:

static void initMXCSR() {
	// Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
	// https://software.intel.com/en-us/node/682949
	_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
	_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
	// Reset other flags
	_MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST);
}

But in any case, your basic point is 100% true. There will be rounding errors, they will accumulate, and different plugins can do things in different ways.

3 Likes

Thanks for the clarification. I learned something new! Now I want to read more about the unsafe math optimizations to better understand what the ramifications are.

Thanks for this thought! I encountered that issue earlier in the project, but realised it was about trigger timing, so solved it with a trigger delay to control the order of the events. However, I hadn’t come across the cable delays info, so that’s useful to know!

In this instance, the complexity all happens before the sequencer, which is only addressed, and then the note triggers are created by the sequencer when a new address is sent. I caught the error while attempting to solve a slightly different problem with a scope, then noticed the effect it was having.

If I continue to have the issue, I will post a patch/screenshot. Thanks for the help!

That’s an elegant solution, too. I think that would create an issue elsewhere because in another element of the same patch I’m referencing specific addresses, which range over two octaves, which would require a voltage range of 24V. That said, it may be useful even for this subsection.

Thanks so much for the help, and ideas!

1 Like

Thanks for this! Who knew a thread about rounding errors could prove so stimulating! :smile:

2 Likes

OK, so my attempt to solve this issue by aiming for the middle of the address didn’t work. I’m going to try @cosinekitty’s suggestion of adding and subtracting whole volts and dividing down the line a go, but I’ll share a stripped back patch that illustrates the issue, both in case I can’t solve it, and because I’m interested in what’s going on!

The situation as is: typically once per “trip” up or down the sequencer, a note is either skipped over or takes two voltage “bumps” in order to change note. The scope is showing net changes of voltage in the region of 100ths of a volt…

The reason I’m using an addressed voltage in this way (instead of triggering fwd/rev), is to allow some notes to change to a specific stage to create leaps (chosen probabilistically in the full patch). The sequencer I’m using doesn’t progress via the trigger input if a cable is plugged into the address input.

The other logic is there to switch between adding a positive or negative voltage to the address when reaching one end of the sequencer (or in the full patch due to a random event), to create a pendulum effect.

The double sample and hold is a previous attempt at solving it, in case there was anything funny going on with the recursive sampling… I’m not sure there was, but it’s there just in case it’s relevant! :man_shrugging:

Hope that’s enough to go on - it’s my first time seeking help with this sort of thing, so I’m wary of adding too much/too little info. If anyone has time to take a look, I’d be very grateful! The patch is so nearly complete, if I can iron out these last wrinkles! :sweat_smile:

OK, so as I’m adding and subtracting nominally 1v, it’s not adding precisely 1v, according to the scope… Could it be an issue with the module that’s introducing the errors?

AHA! I’m an eejit! I was using a unity mixer for combining the CVs, and it was throwing it off… I’d assumed that in a digital environment, I didn’t need a CV specific mixer… Still, it seems I’ve identified the source of the error, and should be ok from here.

Even though the solution wasn’t where I thought it was, I’d not have identified it without following up on advice from this thread, so thanks again!

3 Likes