IEEE extended precision float

Hi,

I need to read and write an 80 bit IEEE extended precision float from
and to AIFF files.

I cant get it to work.

Anyone done this and like to share examples?

Pure ruby preferred. I’d like it to work on both MRI and JRuby.

/Anton

On Jul 6, 2012, at 6:34 AM, Anton H. [email protected]
wrote:

/Anton


Posted via http://www.ruby-forum.com/.

Can you show us what you have tried so far?

Sure.

First, to be more specific, what I’d like is to convert a 80 bit
extended precision float stored in a 10 byte string to and from a ruby
Float. In fact it’s okay if I can convert it to and from a ruby Integer
as I don’t need any decimals. Sample rate of AIFF sound file is stored
as an 80 bit extended precision float. I don’t know why because mostly
this is an integer value and I’d be happy with an Integer.

I’ve tried to convert the read_ieee_extended and write_ieee_extended
methods of the following java class:
http://www.docjar.com/html/api/com/sun/media/sound/AiffFileReader.java.html

I’ve changed float input and output to integer.

Method read_ieee_extended seem to work fine. I’ve tried it with two
different AIF files.

def read_ieee_extended(str)
arr = str.unpack(“n5”)

exponent = arr[0]
hi_mantissa = arr[1] << 16 | arr[2]
lo_mantissa = arr[3] << 16 | arr[4]

if exponent == 0 and hi_mantissa == 0 and lo_mantissa == 0
0
else
if exponent == 0x7FFF
3.40282346638528860e+38
else
exponent -= 16383
exponent -= 31
f = hi_mantissa * (2exponent)
exponent -= 32
f += lo_mantissa * (2
exponent)
f
end
end.to_i
end

Method write_ieee_extended has errors:

def write_ieee_extended(i)
unsigned_short_max = 0b1111111111111111
unsigned_int_max = 0b11111111111111111111111111111111

lo_mantissa = 0
exponent = 16398
hi_mantissa = i

TODO: for now write the integer portion of i

while hi_mantissa < 44000
hi_mantissa *= 2
exponent -= 1
end

hi_mantissa = ((hi_mantissa & unsigned_int_max) << 16) &
unsigned_int_max

arr = [exponent, hi_mantissa >> 16, hi_mantissa & unsigned_short_max,
lo_mantissa >> 16, lo_mantissa & unsigned_short_max]
arr.pack(“n5”)
end

These are quick ugly hacks. Any help is appreciated, thanks. :slight_smile:

/Anton

Anton H. wrote in post #1067670:

I need to read and write an 80 bit IEEE extended precision float from
and to AIFF files.

What do you want to read it into? A ruby Float is just a double, you
will lose precision. Is that acceptable, or do you want to read into a
BigDecimal, or something else?

here also are example usages of the methods above…

irb>a = “@\016\254D\000\000\000\000\000\000” # = 44100
irb>b = “@\017\200\000\000\000\000\000\000\000” # = 65536

irb>read_ieee_extended a
=> 44100
(ok)

irb>read_ieee_extended b
=> 65536
(ok)

irb>write_ieee_extended 44100
=> “@\016\254D\000\000\000\000\000\000”
(ok)

irb>write_ieee_extended 65536
=> “@\016\000\000\000\000\000\000\000\000”
(error)

/Anton

On 07.07.2012 09:11, Anton H. wrote:

I guess this is quite simple. I just cant get my head around the
description in the wikipedia page. Yet.

Maybe GitHub - tadman/raiff: Ruby AIFF Parsing Library can give you an idea …?

HTH

Brian C. wrote in post #1067753:

What do you want to read it into? A ruby Float is just a double, you
will lose precision. Is that acceptable, or do you want to read into a
BigDecimal, or something else?

See my second post. It’s okay to lose precision. In fact, a ruby Integer
value is fine. Sample rates of sound files are typically integers. I
cant understand why a 80 bit float value is used for AIF. The common WAV
file format uses integers for sample rate.

I guess this is quite simple. I just cant get my head around the
description in the wikipedia page. Yet.

/Anton

And for 44100, my C program gives

00 00 00 00 00 00 44 ac 0e 40 00 00 00 00 00 00

(this looks to be the opposite byte ordering of your example)

str="\x00\x00\x00\x00\x00\x00\x44\xac\x0e\x40"
mant, exp = str.unpack(“QS”)
mant * 2.0 ** (exp-16446)
=> 44100.0

Anton H. wrote in post #1067775:

I guess this is quite simple. I just cant get my head around the
description in the wikipedia page. Yet.

Extended precision - Wikipedia

“The standard specifies the minimum requirements for an extended format
but does not specify an encoding.[5] The encoding is the implementor’s
choice.”

So it’s undefined. Do you have some sample values, and know what they
translate to?

As a test, let’s see what GCC gives.

#include <stdio.h>
int main(void)
{
long double foo = 12.5;
unsigned char *p = (unsigned char *)&foo;
int i;
for (i=0; i<sizeof(foo); i++) {
printf(“%02x “, p[i]);
}
printf(”\n”);
return 0;
}

This gives me a value padded to 16 bytes. Taking the first 10 bytes:

str = “\x00\x00\x00\x00\x00\x00\x00\xc8\x02\x40”
mant, exp = str.unpack(“QS”)
=> [14411518807585587200, 16386]
“%064b” % mant
=> “1100100000000000000000000000000000000000000000000000000000000000”
“%016b” % exp
=> “0100000000000010”

(i.e. zero sign, exponent = 16384+2)

The value we want is:

mant / (2.063) * 2.0(exp-16383)
=> 12.5

or equivalently:

mant * 2.0 ** (exp-16446)
=> 12.5

Note that unpack(“Q”) depends on the endianness of the system running
the code, so this isn’t portable to big-endian processors.