Integer is King? - final thoughts about the EQ challenge
-
- KVRian
- 1153 posts since 10 Dec, 2003
One problem is that you are assuming 100% of the error accumulates every iteration / cycle. It doesnt, in fact only a percentage of it does. The amount of error which accumulates depends on the feedback, ie resonance in this case. Thats why filters generaly behave worst at high resonance. The output error has to be fed back in to become compounded, so it's the ratio of input to feedback that determines how much error will be compounded.
And it has a finite limit. If feedack acounts for R% of the input of the input energy then the maximum posible error accumulation is
Sum(r^n)
where r := R%/100, and n = 1..number of iterations,
= ((r^(n+1)) - r) / (r - 1)
Which if n to tends to infinity and r < 1, then we have this.
= -x / (x - 1)
So if feedback acounts for 95% of the input, the maximum cumulated error would be 19 times that of the the error from one itteration.
It can never grow past that.
And it has a finite limit. If feedack acounts for R% of the input of the input energy then the maximum posible error accumulation is
Sum(r^n)
where r := R%/100, and n = 1..number of iterations,
= ((r^(n+1)) - r) / (r - 1)
Which if n to tends to infinity and r < 1, then we have this.
= -x / (x - 1)
So if feedback acounts for 95% of the input, the maximum cumulated error would be 19 times that of the the error from one itteration.
It can never grow past that.
- KVRAF
- 12615 posts since 7 Dec, 2004
what do you mean, i've just calculated the error
the float representation of the number has an error of
0.00000002541812071502541143185416
while the int representation of the same number has an error of
0.0000000002739325828206115792938
the maximum possible error for any number from -1.0 to +1.0 in int is
0.0000000004656612873077392578125
the error for a float representation is dependent upon the scale of the number. very small numbers have lower error than higher numbers, of course. the accuracy of the smallest numbers float can represent (not-denormal) +-5.8e-39 and 1.1e-38 is 127 times better than the accuracy represented between +-0.5 and +-1.0.
actually, it is ironic that the number lines up almost half way between two perfect int fractions making the error nearly 1/2 the worst case for int.
are you saying i've done my math entirely wrong? if you want to show the representations of that number:
exp(-2*pi * 50 / 96000)
in float and int then find the difference in error, that would help to show me where i've gone wrong. i've looked over the math i did and i cant see a single thing wrong with it.
nollock; i didnt assume anything like that. there are several feedback paths available for the error to accumulate in depending upon what type of filter is being calculated. it may be resonance/feedback (every type of effect is a 'filter', i'm not talking about audio filters) or it can be a single integrator. while it can only grow to certain scales in single stages, in a multi-stage system it will accumulate between stages as well.
the float representation of the number has an error of
0.00000002541812071502541143185416
while the int representation of the same number has an error of
0.0000000002739325828206115792938
the maximum possible error for any number from -1.0 to +1.0 in int is
0.0000000004656612873077392578125
the error for a float representation is dependent upon the scale of the number. very small numbers have lower error than higher numbers, of course. the accuracy of the smallest numbers float can represent (not-denormal) +-5.8e-39 and 1.1e-38 is 127 times better than the accuracy represented between +-0.5 and +-1.0.
actually, it is ironic that the number lines up almost half way between two perfect int fractions making the error nearly 1/2 the worst case for int.
are you saying i've done my math entirely wrong? if you want to show the representations of that number:
exp(-2*pi * 50 / 96000)
in float and int then find the difference in error, that would help to show me where i've gone wrong. i've looked over the math i did and i cant see a single thing wrong with it.
nollock; i didnt assume anything like that. there are several feedback paths available for the error to accumulate in depending upon what type of filter is being calculated. it may be resonance/feedback (every type of effect is a 'filter', i'm not talking about audio filters) or it can be a single integrator. while it can only grow to certain scales in single stages, in a multi-stage system it will accumulate between stages as well.
Last edited by aciddose on Sun Jun 10, 2007 4:46 pm, edited 1 time in total.
- u-he
- 30215 posts since 8 Aug, 2002 from Berlin
aciddose, I actually agree*
However, as far as I can see you're only using addition as an example. How would multiplication, pow(), log() and division fit in the picture? I must admit I have no clue how these are done in int64, but given that these operations are used quite often it would be interesting to see how precision is maintained in integer maths...
*edit: on the worst case error, that is.
However, as far as I can see you're only using addition as an example. How would multiplication, pow(), log() and division fit in the picture? I must admit I have no clue how these are done in int64, but given that these operations are used quite often it would be interesting to see how precision is maintained in integer maths...
*edit: on the worst case error, that is.
-
- KVRian
- 770 posts since 2 Apr, 2003
Well that's not really accurate as a statement.aciddose wrote:if you're sticking to a single range like -1.0 ... +1.0, it isnt possible for float to be more accurate than a 32 bit int. in fact, it isnt ever possible in any circumstance for it to be more accurate. it can only be equally accurate in special circumstances, like if you were adding a very small number to a very small number. while that is more accurate for float while you continue working with only very small numbers, immediately when you mix small and large numbers float becomes far less accurate. if you take into consideration real situations (like working in a constantly moving range of numbers between minimum and maximum, small and large, large and small) rather than extreme cases (like adding two tiny numbers) it should be obvious that float is never more accurate than int.
Any stages in your algorithm which have levels more than 48dB below full scale will have worse signal to noise in integer than in float, and you'll never get that back. There's a reason why floating point performance has always been a big requirement for engineering workstations, and that's that the calculations are done in floating point for reasons of accuracy.
-
- KVRian
- 770 posts since 2 Apr, 2003
Oops, seems I was suffering from fumbling fingers on the calculatoraciddose wrote:what do you mean, i've just calculated the error
- KVRAF
- 12615 posts since 7 Dec, 2004
"How would multiplication, pow(), log() and division"
well, we can think of pow as a series of multiplications and log as a series of divisions, so that is simplified.
the error will never be greater than 1/2^31-1 for a multiplication. you multiply 32x32 and get 64. generally you throw away the lower 32 bits (which are actually 0.32) giving quantization error equal to at most 1/2^31-1.
division by fractions in int doesnt make much sense to me since i never do it. it'll take some thinking for me to come up with exactly how that could work. i've never encountered a need to divide anything by a fraction in dsp. due to the amount of calculation required and the twisting of my brain to even think of how it could work it seems any divisions would probably be inefficient.
int isnt very good at non-linear calculations. it's very good for linear calculation. when i need the log of something i use dirty tricks in int rather than trying to get an accurate result. maintaining accuracy would require something like an implementation of floating point in int. non-linear calculations are the whole reason floating point was developed in the first place.
the reason i say int is generally better for dsp is because most of dsp is linear. while you can implement non-linear stuff in int the overhead (handled automatically by the cpu for float) is so much that it by average i'd say will be slower and less accurate than float implementations.
well, we can think of pow as a series of multiplications and log as a series of divisions, so that is simplified.
the error will never be greater than 1/2^31-1 for a multiplication. you multiply 32x32 and get 64. generally you throw away the lower 32 bits (which are actually 0.32) giving quantization error equal to at most 1/2^31-1.
division by fractions in int doesnt make much sense to me since i never do it. it'll take some thinking for me to come up with exactly how that could work. i've never encountered a need to divide anything by a fraction in dsp. due to the amount of calculation required and the twisting of my brain to even think of how it could work it seems any divisions would probably be inefficient.
int isnt very good at non-linear calculations. it's very good for linear calculation. when i need the log of something i use dirty tricks in int rather than trying to get an accurate result. maintaining accuracy would require something like an implementation of floating point in int. non-linear calculations are the whole reason floating point was developed in the first place.
the reason i say int is generally better for dsp is because most of dsp is linear. while you can implement non-linear stuff in int the overhead (handled automatically by the cpu for float) is so much that it by average i'd say will be slower and less accurate than float implementations.
- KVRAF
- 12615 posts since 7 Dec, 2004
and the code i wrote quickly to pull out the float/double values
http://xhip.cjb.net/temp/public/f.c
that might be useful to somebody (and maybe i made a mistake in there?)
all this stuff is just off the top of my head and i havent spent much time studying it. i'm just trying to come up with explanations for several observations i've made over time working in dsp. i was especially surprised that thorkz really did pick out the int version and i'm trying to come up with a math-based explanation for that. it could be any number of things and it may actually be something far more simple than the stuff i'm theorizing about.
i'm only coming to these conclusions because they match up with the experience i've had with my own code over time and i like the idea of a neat unified explanation for exactly what the differences between float and int are, and what each format is good and bad at.
Christian; i've been thinking for a while now that another experiment could be that you run the filters over top of duplicate input signals and then find the differences between all three. it would be interesting to see if distinct behaviours could be observed that might be attributed to being float or int.
maybe a double, float, int 64 and int 32 set would allow that.
http://xhip.cjb.net/temp/public/f.c
that might be useful to somebody (and maybe i made a mistake in there?)
all this stuff is just off the top of my head and i havent spent much time studying it. i'm just trying to come up with explanations for several observations i've made over time working in dsp. i was especially surprised that thorkz really did pick out the int version and i'm trying to come up with a math-based explanation for that. it could be any number of things and it may actually be something far more simple than the stuff i'm theorizing about.
i'm only coming to these conclusions because they match up with the experience i've had with my own code over time and i like the idea of a neat unified explanation for exactly what the differences between float and int are, and what each format is good and bad at.
Christian; i've been thinking for a while now that another experiment could be that you run the filters over top of duplicate input signals and then find the differences between all three. it would be interesting to see if distinct behaviours could be observed that might be attributed to being float or int.
maybe a double, float, int 64 and int 32 set would allow that.
- KVRAF
- 12615 posts since 7 Dec, 2004
i said about float accuracy.. the smallest number "is 127 times better than" 1.0.
er.. i forgot the ^ and it is 2^127 times better which is _quite_ different. it doesnt really matter for anything i said in the post though.
"actually, it is ironic that the number lines up almost half way"
i seem to have been thinking two things while writing that. i meant, it is ironic that it lines up directly between because it is almost exactly the maximum error possible for the int representation. the value i've been posting (1/2^31) is actually wrong since the maximum error is going to be half of the distance between any two points. so, the error is actually at most (1/2^32) which is almost exactly what the error for the number i used is.
er.. i forgot the ^ and it is 2^127 times better which is _quite_ different. it doesnt really matter for anything i said in the post though.
"actually, it is ironic that the number lines up almost half way"
i seem to have been thinking two things while writing that. i meant, it is ironic that it lines up directly between because it is almost exactly the maximum error possible for the int representation. the value i've been posting (1/2^31) is actually wrong since the maximum error is going to be half of the distance between any two points. so, the error is actually at most (1/2^32) which is almost exactly what the error for the number i used is.
-
- KVRian
- 1153 posts since 10 Dec, 2003
Isn't that implying accumulation of 100%, (1 bit) of the error per cycle?aciddose wrote:remember, over the period of one cycle it becomes -102db, and over two cycles already -96db if the error is summed every cycle. if it is multiplied.. then it requires only 17 cycles to become 100%.
Yes but unless it is itterative error, as in feedback, the error will be next to nothing in comparison. Stages in series or paralell will only *add up* error, not recursively multiply it as with feedback.there are several feedback paths available for the error to accumulate in depending upon what type of filter is being calculated. it may be resonance/feedback (every type of effect is a 'filter', i'm not talking about audio filters) or it can be a single integrator. while it can only grow to certain scales in single stages, in a multi-stage system it will accumulate between stages as well.
If you have feeback across multiple stages then it does get a bit more complicated, but only so much as in have to treat it as the maximum error* from the feedback multiplied by the maximum error from the contained stages.
edit: *Ie the cumulative error factor posted earlier.
Last edited by nollock on Sun Jun 10, 2007 6:00 pm, edited 2 times in total.
-
- KVRAF
- 3080 posts since 17 Apr, 2005 from S.E. TN
I don't want to drift Christian's thread, apologies. Just a clarification on the impedance thang. Just wanted to caution on the impedance ratio topic in case some reader would become misinformed.
There is the old parable (in many unattributed variations) about scholars vigorously debating the number of teeth in a horse's mouth. A young man suggests looking in the horse's mouth and counting them. Experimental method, as in Christian's excellent experiment. As opposed to endlessly debating whether an effect is theoretically audible.
That is why I gave you a specific prescription for verifying one instance where hi-z inputs are bad. Just wire up a breadboard and replicate the experiment if you are curious enough. I specified a condenser mic because that makes it an active electronically buffered signal source with most modern mics (fulfilling yer objection about passive signal sources). I specified phantom power because it makes the noise so loud that it is not easily ignored. But you can hear less-loud-but-still-annoying mechanical noise problems even without phantom power, and even without a mic plugged in.
In the case of the phantom power and very high input impedance, I suspect that the cable-handling noise is primarily because the phantom power turns the cable into a low-level condenser mic. It ALWAYS turns the mic cable into a condenser mic, but with an ordinary mic preamp input impedance in the ballpark of 2 KOhm or whatever, the voltages from the microphonic cable do not have enough current drive to contribute much to the signal. The low preamp impedance 'eats the cable noise' because he cable noise has extremely small current drive. IOW, the cable noise is a relatively-high-voltage-very-low-power signal.
However, the microphonic cable voltage output is pretty big when it is not loaded down by a fairly low impedance mic preamp. With a really high impedance preamp, in the 'instrumentation amp impedance range', the low power of the cable noise is no problem, and the preamp amplifies that handling noise voltage quite happily.
That is just my dumb theory and maybe there is a better explanation. I'm not pushing my pet theory. Just pointing out the that you don't always want to design-in the highest impedance electronically possible. There are other considerations than just avoiding a tiny-fraction-of-a-dB interstage loss. In many cases a 1-to-10 Output-vs-Input impedance ratio is 'close enough for rock'n'roll', but there are many cases you might want some other ratio.
Since typical mics are in the ballpark of 150 ohms, and typical transformerless balanced preamps are in the ballpark of 2000 ohms, that's kinda like the rule-of-thumb 1-to-10 rule immortalized in zillions of commercial mixers.
There is the old parable (in many unattributed variations) about scholars vigorously debating the number of teeth in a horse's mouth. A young man suggests looking in the horse's mouth and counting them. Experimental method, as in Christian's excellent experiment. As opposed to endlessly debating whether an effect is theoretically audible.
That is why I gave you a specific prescription for verifying one instance where hi-z inputs are bad. Just wire up a breadboard and replicate the experiment if you are curious enough. I specified a condenser mic because that makes it an active electronically buffered signal source with most modern mics (fulfilling yer objection about passive signal sources). I specified phantom power because it makes the noise so loud that it is not easily ignored. But you can hear less-loud-but-still-annoying mechanical noise problems even without phantom power, and even without a mic plugged in.
In the case of the phantom power and very high input impedance, I suspect that the cable-handling noise is primarily because the phantom power turns the cable into a low-level condenser mic. It ALWAYS turns the mic cable into a condenser mic, but with an ordinary mic preamp input impedance in the ballpark of 2 KOhm or whatever, the voltages from the microphonic cable do not have enough current drive to contribute much to the signal. The low preamp impedance 'eats the cable noise' because he cable noise has extremely small current drive. IOW, the cable noise is a relatively-high-voltage-very-low-power signal.
However, the microphonic cable voltage output is pretty big when it is not loaded down by a fairly low impedance mic preamp. With a really high impedance preamp, in the 'instrumentation amp impedance range', the low power of the cable noise is no problem, and the preamp amplifies that handling noise voltage quite happily.
That is just my dumb theory and maybe there is a better explanation. I'm not pushing my pet theory. Just pointing out the that you don't always want to design-in the highest impedance electronically possible. There are other considerations than just avoiding a tiny-fraction-of-a-dB interstage loss. In many cases a 1-to-10 Output-vs-Input impedance ratio is 'close enough for rock'n'roll', but there are many cases you might want some other ratio.
Since typical mics are in the ballpark of 150 ohms, and typical transformerless balanced preamps are in the ballpark of 2000 ohms, that's kinda like the rule-of-thumb 1-to-10 rule immortalized in zillions of commercial mixers.
-
- KVRist
- 213 posts since 30 Dec, 2006 from Darmstadt, Germany
actually it's quite simple, but just having implemented division by fractions a few days ago, i can say, yes, it is *very* inefficient, because a 64->64bit division is required (the 64->32bit division is all that's provided by the x86 cpu and it won't work unless you don't mind exceptions...)aciddose wrote:division by fractions in int doesnt make much sense to me since i never do it. it'll take some thinking for me to come up with exactly how that could work. i've never encountered a need to divide anything by a fraction in dsp. due to the amount of calculation required and the twisting of my brain to even think of how it could work it seems any divisions would probably be inefficient.
basically the algo is:
let a be a q.fa fixed-point number, b a q.fb fixed-point number
to compute a/b:
a << fb
a / b (64 bit division)
deceptively simple right? but when actually implementing it, it get's real ugly.
of course if you dont care about precision, do the following (which is fast, and simple):
res = a / b (normal integer division)
res << fb
-
- KVRist
- 86 posts since 14 Jan, 2007 from around
Yeah, its all really funny to listen to the int64 filter now that I know what it is and not beeing in a hurry because a really nice threat is going steeply downhill. I assume I am listening closer now.Christian Budde wrote:Btw. the SNR for the 24bit version was only -79dB. Just as a reason, why thorKz might have heard that difference.
However, if thorKz is still around, could you please tell us, what settings you used for the EQ? Did you use some extreme settings? Or only peak EQs? The type of filter is at least important to make the 24bit version even worse. Especially if you attenuate and gain again.
The int64 sounds a lot different from the int32 though I must say.
During the first test I think I was mainly using peak filters, boosted pretty high so I could hear them. The cuts I have discovered now seemed to be just bypassed knobs from another of your projects.
Woow. I see now that every band has all the filters to choose from. A great plugin that would make.
I think I used the Lo/Hi-shelfs also though. Basicly the filters how they are accessible from default.
thorkz
PS: You can ask mauseleum too. He hears it. Probably better than I do.
Last edited by thorkz on Sun Jun 10, 2007 6:25 pm, edited 1 time in total.
- KVRAF
- 12615 posts since 7 Dec, 2004
ah right. that is actually one of the first things that came to me, i was thinking "division should be exactly like multiplication, only reversed". i actually do use a lot of divisions like this in my graphics code, i just never thought of it as being a real fractional division.
it's a shame the x86 doesnt pay more attention to higher level math in int with ops like 64 bit divs and opcodes to automatically scale and then drop bits (1:0.31 x 1:0.31 = 1:0.31). a lot of the time the work done to get something like a fractional int div going is mostly just overhead while the calculations are all single-cycle and many could be parallel.
maybe that'll come eventually, but i think it is more likely we'll end up working with qubits or something worse instead. (are those qubits ints or floats?
)
it's a shame the x86 doesnt pay more attention to higher level math in int with ops like 64 bit divs and opcodes to automatically scale and then drop bits (1:0.31 x 1:0.31 = 1:0.31). a lot of the time the work done to get something like a fractional int div going is mostly just overhead while the calculations are all single-cycle and many could be parallel.
maybe that'll come eventually, but i think it is more likely we'll end up working with qubits or something worse instead. (are those qubits ints or floats?
-
- KVRist
- 213 posts since 30 Dec, 2006 from Darmstadt, Germany
yeah, it's got a huge instruction set but half of it (especially when counting all the extensions) is barely ever needed (or just not used by many compilers). but things that actually might be useful like fixed-point computation and saturating arithmetic* (for image processing and maybe also audio) aren't included. (and both would be *very* simple to implement in hardware)aciddose wrote:ah right. that is actually one of the first things that came to me, i was thinking "division should be exactly like multiplication, only reversed". i actually do use a lot of divisions like this in my graphics code, i just never thought of it as being a real fractional division.
it's a shame the x86 doesnt pay more attention to higher level math in int with ops like 64 bit divs and opcodes to automatically scale and then drop bits (1:0.31 x 1:0.31 = 1:0.31). a lot of the time the work done to get something like a fractional int div going is mostly just overhead while the calculations are all single-cycle and many could be parallel.
and long before qubits come around, well all have to wrap our heads around massive concurrency, oh the fun!maybe that'll come eventually, but i think it is more likely we'll end up working with qubits or something worse instead. (are those qubits ints or floats?)
*: ok, it does have saturating 16-bit addition/subtraction in MMX and SSE2, but no multiplication, division, or higher (or lower) bit counts
-
- KVRist
- 89 posts since 24 May, 2005 from Sweden

