[sip-comm-dev] gsoc - wideband codec


#1

Hi Lubo, Jean and Emil,

I have finished most of the coding translation work of CELT, and now I am
testing several modules to make sure it works fine.

I have successful tested the MDCT module (attachment), and have questions
about the floating point precision.

My test result is
nfft=32 inverse=0, snr = 119.206488
nfft=32 inverse=1, snr = 117.467516
nfft=256 inverse=0, snr = 101.882718
nfft=256 inverse=1, snr = 98.382578
nfft=512 inverse=0, snr = 95.132894
nfft=512 inverse=1, snr = 93.272120
nfft=40 inverse=0, snr = 122.074640
nfft=40 inverse=1, snr = 116.079816
nfft=56 inverse=0, snr = 113.834607
nfft=56 inverse=1, snr = 111.845744
nfft=120 inverse=0, snr = 107.991940
nfft=120 inverse=1, snr = 104.997673
nfft=240 inverse=0, snr = 103.428031
nfft=240 inverse=1, snr = 99.625043
nfft=480 inverse=0, snr = 96.865991
nfft=480 inverse=1, snr = 93.639277
(The snr refers to signal to noise ratio in dB, and here represents the
computation errors)

My result is worse than the result of the c code, whose snr is generally
around 140dB. And from the above results we can see, the longer the nfft is,
the worse snr it brings. So I guess this is caused by the error accumulate.
But my result is still OK, because from the author's mdct-test.c, the

60dBresult is correct.

I use float type during the algorithm, just exactly like the c. And as far
as I know, the format of float type in c and java is the same(IEEE 754). So
I don't understand why they have different errors. I remember both Lubo and
Jean metioned the floating point precition problem.

Now I don't know how badly the float precision errors affect the codec.
Later I'll see.

Cheers
Jing

Mdct_test.java (4.49 KB)

Mdct.java (9.51 KB)

Kiss_fft.java (26.8 KB)


#2

Hi Jing,

I have finished most of the coding translation work of CELT, and now I am
testing several modules to make sure it works fine.

Good news :slight_smile:

My result is worse than the result of the c code, whose snr is generally
around 140dB. And from the above results we can see, the longer the nfft is,
the worse snr it brings. So I guess this is caused by the error accumulate.
But my result is still OK, because from the author's mdct-test.c, the >60dB
result is correct.

Of course, being above the 'poor-ness' threshold is great, but it
would be even better if we can find out where this discrepancy comes
from !

I use float type during the algorithm, just exactly like the c. And as far
as I know, the format of float type in c and java is the same(IEEE 754). So

Technically speaking, IEEE 754 is also a container, somewhat like AVI
files can contain anything from raw images to xvid bitstream.
So IEEE 754 defines several formats, the most famous two being
'simple' and 'double' precision. They are respectively 32 and 64 bits
wide. In C language, float is IEEE 754's simple precision, and double
is IEEE 754's double precision. This can be easily checked, and it is
the case at least on my machines. For Java, as far as I know, it is
the same, as suggested here
http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html

I don't understand why they have different errors. I remember both Lubo and
Jean metioned the floating point precition problem.

I wish I hadn't, because now that I have investigated the topic, I
feel pretty upset :stuck_out_tongue:

Now I don't know how badly the float precision errors affect the codec.
Later I'll see.

So, why do the computation results differ ?! There is no easy answer,
however here is a quote :
"Java’s floating-point expression-evaluation turns into Numerical Junk".

Contrarily to popular (and Emil's) belief, I did not say that, William
Kahan did in [1]. And he's much more commendable than me. I suggest
that you do not read it now though, because it is 81 pages, but you
can put it on your bedside table....

And now it's time to talk about Java and IEEE-754 again. In the early
days, Java was going to take over the universe, and one of the
necessary things to achieve was reproducible results. This sounds fair
for almost all daily computations except floating points. Why ?

Because floating point units differ from one hardware to another. And
even though Apple has come back from the dark side of the chip and
joined the Intel/AMD/others/x86 conspiracy, there are still some
machines out there with FPU that do not run like Intel's x87. Just
think about all those arm cores for example.

Here comes IEEE-754, it is a standard after all. So we can safely say
that Java gives reproducible results on all standard conformant
systems. Unfortunately, as I told you earlier, there are several bit
types defined in IEEE-754 standards, one of them being 'long double'
which is at least 80 bits.

And this type is used internally by the x87 FPU to do *ANY* floating
point calculation. The result is then rounded to whatever bit format
you were asking for, by the compiler (see [2]).

But this behavior is not common place (although not a standard
violation either), so a result on a x86 differs from a result on, say,
SPARC, Alpha, MIPS, PPC, etc... What a mess. Fortunately you can issue
an assembly command to constrain the hardware to 'internal double'
precision only, instead of long double.

As a side note, [2] suggests that BSD systems activate this behavior
by default so I would be interested if someone with a BSD box
(possibly not a virtual one) could fetch libcelt and run the mdct-test
(from tests/ directory) for me. I will attach my results below for
comparison. Now that I think of it, darwin systems migh work like
BSDs, or they might not. I attached the results from a friend's
intel-mac, just for fun.

So, what should we do now ? My first suggestion is to convert
everything to double, to see if you get better results (this is
suggested by [1] because all-float calculations are rounded to float
thus losing precision and accuracy). This will have NO impact on the
processing because most of the hardware do the internal computations
using double anyway. I would do it but I seem to be missing the Config
class from your archive and am too lazy to investigate it, hehehe.

Sorry for that long email, and good luck with that FPU mess. By the
way, this is just an explanation attempt, which means I can be wrong,
or most likely just party true.

Jean

PS: java standard might have evolved and support long double, but I am
not aware of this fact, someone correct me if I'm wrong.

References :
[1] http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf
[2] http://www.network-theory.co.uk/docs/gccintro/gccintro_70.html

Sample results for mdct-test from libcelt's mdct-test.c (dunno the
libcelt version, apparently fetched on April 9th)
nfft=32 inverse=0,snr = 145.219638
nfft=32 inverse=1,snr = 144.590286
nfft=256 inverse=0,snr = 141.993721
nfft=256 inverse=1,snr = 142.784441
nfft=512 inverse=0,snr = 141.123660
nfft=512 inverse=1,snr = 141.128761
nfft=40 inverse=0,snr = 143.958780
nfft=40 inverse=1,snr = 141.627641
nfft=56 inverse=0,snr = 142.992619
nfft=56 inverse=1,snr = 145.087616
nfft=120 inverse=0,snr = 140.111970
nfft=120 inverse=1,snr = 142.695879
nfft=240 inverse=0,snr = 139.789205
nfft=240 inverse=1,snr = 141.727412
nfft=480 inverse=0,snr = 140.229189
nfft=480 inverse=1,snr = 142.468349
with [2]'s -msse2 -mfpmath=sse compilation flags on same hardware
nfft=32 inverse=0,snr = 142.311990
nfft=32 inverse=1,snr = 144.019064
nfft=256 inverse=0,snr = 139.444941
nfft=256 inverse=1,snr = 138.041439
nfft=512 inverse=0,snr = 139.488676
nfft=512 inverse=1,snr = 139.153238
nfft=40 inverse=0,snr = 141.065740
nfft=40 inverse=1,snr = 139.050118
nfft=56 inverse=0,snr = 137.236628
nfft=56 inverse=1,snr = 141.151461
nfft=120 inverse=0,snr = 137.361481
nfft=120 inverse=1,snr = 140.720815
nfft=240 inverse=0,snr = 138.391179
nfft=240 inverse=1,snr = 139.046024
nfft=480 inverse=0,snr = 137.307414
nfft=480 inverse=1,snr = 138.920736
Processor info, model name :Intel(R) Core(TM)2 Duo CPU E4700 @ 2.60GHz
OS Info: Debian Linux, kernel 2.6.24, glibc 2.3.6, gcc 4.3.2

Sample results for mdct-test from libcelt's mdct-test.c (celt 0.7.1)
nfft=32 inverse=0,snr = 140.440378
nfft=32 inverse=1,snr = 143.876233
nfft=256 inverse=0,snr = 138.890405
nfft=256 inverse=1,snr = 138.232467
nfft=512 inverse=0,snr = 138.457512
nfft=512 inverse=1,snr = 139.477586
nfft=40 inverse=0,snr = 137.242465
nfft=40 inverse=1,snr = 140.650695
nfft=56 inverse=0,snr = 138.458400
nfft=56 inverse=1,snr = 140.707211
nfft=120 inverse=0,snr = 137.187369
nfft=120 inverse=1,snr = 140.710089
nfft=240 inverse=0,snr = 138.688972
nfft=240 inverse=1,snr = 138.796247
nfft=480 inverse=0,snr = 136.993939
nfft=480 inverse=1,snr = 138.320163
Changing compiler flags yields same results
Processor info, Intel Core 2 Duo @ 3.06GHz
OS Info : OSX 10.6.4, Darwin 10.4.0, gcc 4.2.1

···

Cheers
Jing

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net


#3

Hi Jean,

Thanks for your detailed and very valuable investigation. A very good lesson
for me.

there are several bit
types defined in IEEE-754 standards, one of them being 'long double'
which is at least 80 bits.
And this type is used internally by the x87 FPU to do *ANY* floating
point calculation.

Key point. I wonder how the JVM act on x87 too.

The result is then rounded to whatever bit format
you were asking for, by the compiler (see [2]).

BTW, I have used TI C55x before(for not a long time), which in fact doesn't
have a FPU, and treat any floating point as 32bit single precision by CPU
with the help of compiler, bigger problems for precision.
But as I look into the libcelt, it supports C5x platform, so I guess maybe
this is not really a problem :slight_smile:

So, what should we do now ? My first suggestion is to convert
everything to double, to see if you get better results (this is
suggested by [1] because all-float calculations are rounded to float
thus losing precision and accuracy). This will have NO impact on the
processing because most of the hardware do the internal computations
using double anyway. I would do it but I seem to be missing the Config
class from your archive and am too lazy to investigate it, hehehe.

I'll try that when I finish celt, or when I realize it will have a big and
bad effect on the entile codec.

···

2010/7/2 Jean Lorchat <jean.lorchat@gmail.com>

PS: java standard might have evolved and support long double, but I am
not aware of this fact, someone correct me if I'm wrong.