From dc4a82eeed63b8bab916faddb5fffb76f350065e Mon Sep 17 00:00:00 2001 From: Mattias Wadman Date: Thu, 9 Mar 2023 14:02:59 +0100 Subject: [PATCH] aiff: Add basic decoder --- README.md | 1 + doc/formats.md | 3 +- doc/formats.svg | 880 ++++++++++++++------------- format/all/all.fqtest | 2 + format/format.go | 1 + format/riff/aiff.go | 117 ++++ format/riff/testdata/sox.aiff | Bin 0 -> 8908 bytes format/riff/testdata/sox.aiff.fqtest | 35 ++ internal/mathex/float80.go | 215 +++++++ 9 files changed, 819 insertions(+), 435 deletions(-) create mode 100644 format/riff/aiff.go create mode 100644 format/riff/testdata/sox.aiff create mode 100644 format/riff/testdata/sox.aiff.fqtest create mode 100644 internal/mathex/float80.go diff --git a/README.md b/README.md index a2fa0e5e..1d0f88c6 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ In summary it aims to be jq, hexdump, dd and gdb for files combined into one. [aac_frame](doc/formats.md#aac_frame), adts, adts_frame, +aiff, amf0, apev2, [apple_bookmark](doc/formats.md#apple_bookmark), diff --git a/doc/formats.md b/doc/formats.md index 4ce07db4..e7e51a67 100644 --- a/doc/formats.md +++ b/doc/formats.md @@ -7,6 +7,7 @@ |[`aac_frame`](#aac_frame) |Advanced Audio Coding frame || |`adts` |Audio Data Transport Stream |`adts_frame`| |`adts_frame` |Audio Data Transport Stream frame |`aac_frame`| +|`aiff` |Audio Interchange File Format || |`amf0` |Action Message Format 0 || |`apev2` |APEv2 metadata tag |`image`| |[`apple_bookmark`](#apple_bookmark) |Apple BookmarkData || @@ -123,7 +124,7 @@ |`ip_packet` |Group |`icmp` `icmpv6` `tcp_segment` `udp_datagram`| |`link_frame` |Group |`bsd_loopback_frame` `ether8023_frame` `ipv4_packet` `ipv6_packet` `sll2_packet` `sll_packet`| |`mp3_frame_tags` |Group |`mp3_frame_vbri` `mp3_frame_xing`| -|`probe` |Group |`adts` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `wasm` `wav` `webp` `xml` `yaml` `zip`| +|`probe` |Group |`adts` `aiff` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `wasm` `wav` `webp` `xml` `yaml` `zip`| |`tcp_stream` |Group |`dns_tcp` `rtmp` `tls`| |`udp_payload` |Group |`dns`| diff --git a/doc/formats.svg b/doc/formats.svg index ebb629bb..63bc3eec 100644 --- a/doc/formats.svg +++ b/doc/formats.svg @@ -4,11 +4,11 @@ - + formats - + adts @@ -46,10 +46,10 @@ apev2 - -apev2 - -image + +apev2 + +image @@ -60,8 +60,8 @@ apev2:e->image:n - - + + @@ -74,7 +74,7 @@ icc_profile - + image->jpeg:n @@ -134,7 +134,7 @@ vpx_ccr - + image->mp4:n @@ -150,7 +150,7 @@ exif - + image->png:n @@ -164,7 +164,7 @@ icc_profile - + image->tiff:n @@ -178,22 +178,22 @@ vp8_frame - + image->webp:n - + gif - -gif + +gif - + image->gif:n - - + + @@ -206,25 +206,25 @@ probe - -probe + +probe ar:e->probe:n - - + + probe->adts:n - - + + - + probe->ar:n - + @@ -242,9 +242,9 @@ flac_frame - + probe->avi:n - + @@ -256,10 +256,10 @@ bitcoin_block - + probe->bitcoin_blkdat:n - - + + @@ -270,9 +270,9 @@ probe - + probe->bzip2:n - + @@ -286,9 +286,9 @@ flac_frame - + probe->flac:n - + @@ -300,15 +300,15 @@ probe - + probe->gzip:n - - + + - + probe->jpeg:n - + @@ -320,10 +320,10 @@ macho - + probe->macho_fat:n - - + + @@ -332,9 +332,9 @@ macho - + probe->macho:n - + @@ -382,43 +382,43 @@ vp9_frame - + probe->matroska:n - + mp3 - -mp3 - -id3v2 - -id3v1 - -id3v11 - -apev2 - -mp3_frame + +mp3 + +id3v2 + +id3v1 + +id3v11 + +apev2 + +mp3_frame - + probe->mp3:n - - + + - + probe->mp4:n - - + + - + probe->png:n - + @@ -438,254 +438,266 @@ flac_frame - + probe->ogg:n - + pcap - -pcap - -link_frame - -tcp_stream - -ipv4_packet + +pcap + +link_frame + +tcp_stream + +ipv4_packet - + probe->pcap:n - - + + pcapng - -pcapng - -link_frame - -tcp_stream - -ipv4_packet + +pcapng + +link_frame + +tcp_stream + +ipv4_packet - + probe->pcapng:n - - + + tar - -tar - -probe + +tar + +probe - + probe->tar:n - - + + - + probe->tiff:n - - + + wav - -wav - -id3v2 - -id3v1 - -id3v11 + +wav + +id3v2 + +id3v1 + +id3v11 - + probe->wav:n - - + + - + probe->webp:n - + zip - -zip - -probe + +zip + +probe - + probe->zip:n - - + + + + + +aiff + +aiff + + + +probe->aiff:n + + - + apple_bookmark - -apple_bookmark + +apple_bookmark - + probe->apple_bookmark:n - - + + - + avro_ocf - -avro_ocf + +avro_ocf - + probe->avro_ocf:n - - + + - + bplist - -bplist + +bplist - + probe->bplist:n - - + + - + elf - -elf + +elf - + probe->elf:n - - + + - + probe->gif:n - - + + - + json - -json + +json - + probe->json:n - - + + - + jsonl - -jsonl + +jsonl - + probe->jsonl:n - - + + - + mpeg_ts - -mpeg_ts + +mpeg_ts - + probe->mpeg_ts:n - - + + - + toml - -toml + +toml - + probe->toml:n - - + + - + tzif - -tzif + +tzif - + probe->tzif:n - - + + - + wasm - -wasm + +wasm - + probe->wasm:n - - + + - + xml - -xml + +xml - + probe->xml:n - - + + - + yaml - -yaml + +yaml - + probe->yaml:n - - + + @@ -916,70 +928,70 @@ bsd_loopback_frame - -bsd_loopback_frame - -inet_packet + +bsd_loopback_frame + +inet_packet inet_packet - -inet_packet + +inet_packet bsd_loopback_frame:e->inet_packet:n - - + + ipv4_packet - -ipv4_packet - -ip_packet + +ipv4_packet + +ip_packet - + inet_packet->ipv4_packet:n - - + + ipv6_packet - -ipv6_packet - -ip_packet + +ipv6_packet + +ip_packet - + inet_packet->ipv6_packet:n - - + + bzip2:e->probe:n - - + + ether8023_frame - -ether8023_frame - -inet_packet + +ether8023_frame + +inet_packet ether8023_frame:e->inet_packet:n - - + + @@ -1062,8 +1074,8 @@ flac_picture:e->image:n - - + + @@ -1074,8 +1086,8 @@ gzip:e->probe:n - - + + @@ -1158,94 +1170,94 @@ ip_packet - -ip_packet + +ip_packet ipv4_packet:e->ip_packet:n - - + + udp_datagram - -udp_datagram - -udp_payload + +udp_datagram + +udp_payload - + ip_packet->udp_datagram:n - - + + - + icmp - -icmp + +icmp - + ip_packet->icmp:n - - + + - + icmpv6 - -icmpv6 + +icmpv6 - + ip_packet->icmpv6:n - - + + - + tcp_segment - -tcp_segment + +tcp_segment - + ip_packet->tcp_segment:n - - + + ipv6_packet:e->ip_packet:n - - + + exif - -exif + +exif jpeg:e->exif:n - - + + icc_profile - -icc_profile + +icc_profile jpeg:e->icc_profile:n - - + + @@ -1262,8 +1274,8 @@ matroska:e->image:n - - + + @@ -1440,65 +1452,65 @@ mp3:e->apev2:n - - + + mp3:e->mp3_frame:n - + mp3:e->id3v2:n - - + + id3v1 - -id3v1 + +id3v1 mp3:e->id3v1:n - - + + id3v11 - -id3v11 + +id3v11 mp3:e->id3v11:n - - + + - + mp3_frame_vbri mp3_frame_vbri - + mp3_frame_tags->mp3_frame_vbri:n - + mp3_frame_xing mp3_frame_xing - + mp3_frame_tags->mp3_frame_xing:n @@ -1578,8 +1590,8 @@ mp4:e->icc_profile:n - - + + @@ -1698,14 +1710,14 @@ png:e->exif:n - - + + png:e->icc_profile:n - - + + @@ -1722,23 +1734,23 @@ mpeg_pes - -mpeg_pes - -mpeg_pes_packet - -mpeg_spu + +mpeg_pes + +mpeg_pes_packet + +mpeg_spu mpeg_pes:e->mpeg_pes_packet:n - + mpeg_pes:e->mpeg_spu:n - + @@ -1780,240 +1792,240 @@ pcap:e->ipv4_packet:n - - + + link_frame - -link_frame + +link_frame pcap:e->link_frame:n - - + + tcp_stream - -tcp_stream + +tcp_stream pcap:e->tcp_stream:n - - + + - + link_frame->bsd_loopback_frame:n - - + + - + link_frame->ether8023_frame:n - - + + - + link_frame->ipv4_packet:n - - + + - + link_frame->ipv6_packet:n - - + + sll2_packet - -sll2_packet - -inet_packet + +sll2_packet + +inet_packet - + link_frame->sll2_packet:n - - + + sll_packet - -sll_packet - -inet_packet + +sll_packet + +inet_packet - + link_frame->sll_packet:n - - + + rtmp - -rtmp - -amf0 - -mpeg_asc + +rtmp + +amf0 + +mpeg_asc - + tcp_stream->rtmp:n - - + + tls - -tls - -asn1_ber + +tls + +asn1_ber - + tcp_stream->tls:n - - + + - + dns_tcp - -dns_tcp + +dns_tcp - + tcp_stream->dns_tcp:n - - + + pcapng:e->ipv4_packet:n - - + + pcapng:e->link_frame:n - - + + pcapng:e->tcp_stream:n - - + + rtmp:e->mpeg_asc:n - - + + amf0 - -amf0 + +amf0 rtmp:e->amf0:n - - + + sll2_packet:e->inet_packet:n - - + + sll_packet:e->inet_packet:n - - + + tar:e->probe:n - - + + tiff:e->icc_profile:n - - + + asn1_ber - -asn1_ber + +asn1_ber tls:e->asn1_ber:n - - + + udp_payload - -udp_payload + +udp_payload udp_datagram:e->udp_payload:n - - + + - + dns - -dns + +dns - + udp_payload->dns:n - - + + wav:e->id3v2:n - - + + wav:e->id3v1:n - - + + wav:e->id3v11:n - - + + @@ -2024,68 +2036,68 @@ zip:e->probe:n - - + + - + bencode - -bencode + +bencode - + bits - -bits + +bits - + bson - -bson + +bson - + bytes - -bytes + +bytes - + cbor - -cbor + +cbor - + csv - -csv + +csv - + fairplay_spc - -fairplay_spc + +fairplay_spc - + html - -html + +html - + markdown - -markdown + +markdown - + msgpack - -msgpack + +msgpack diff --git a/format/all/all.fqtest b/format/all/all.fqtest index 196f0c2e..068d8f7e 100644 --- a/format/all/all.fqtest +++ b/format/all/all.fqtest @@ -27,6 +27,7 @@ $ fq -n _registry.groups.probe "wasm", "webp", "zip", + "aiff", "mp3", "mpeg_ts", "wav", @@ -40,6 +41,7 @@ $ fq --help formats aac_frame Advanced Audio Coding frame adts Audio Data Transport Stream adts_frame Audio Data Transport Stream frame +aiff Audio Interchange File Format amf0 Action Message Format 0 apev2 APEv2 metadata tag apple_bookmark Apple BookmarkData diff --git a/format/format.go b/format/format.go index 9d9e61b9..19cfd6f2 100644 --- a/format/format.go +++ b/format/format.go @@ -29,6 +29,7 @@ const ( AAC_FRAME = "aac_frame" ADTS = "adts" ADTS_FRAME = "adts_frame" + AIFF = "aiff" AMF0 = "amf0" APEV2 = "apev2" APPLE_BOOKMARK = "apple_bookmark" diff --git a/format/riff/aiff.go b/format/riff/aiff.go new file mode 100644 index 00000000..e4430669 --- /dev/null +++ b/format/riff/aiff.go @@ -0,0 +1,117 @@ +package riff + +// http://midi.teragonaudio.com/tech/aiff.htm + +import ( + "github.com/wader/fq/format" + "github.com/wader/fq/internal/mathex" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" + "github.com/wader/fq/pkg/scalar" +) + +func init() { + interp.RegisterFormat(decode.Format{ + Name: format.AIFF, + ProbeOrder: format.ProbeOrderBinFuzzy, + Description: "Audio Interchange File Format", + Groups: []string{format.PROBE}, + DecodeFn: aiffDecode, + }) +} + +const aiffRiffType = "AIFF" + +// pstring: +// > Pascal-style string, a one-byte count followed by that many text bytes. The total number of bytes in this data type should be even. +// > A pad byte can be added to the end of the text to accomplish this. This pad byte is not reflected in the count. +func aiffPString(d *decode.D) string { + l := d.U8() + pad := (l + 1) % 2 + s := d.UTF8(int(l + pad)) + return s[0 : l+1-pad] +} + +func aiffDecode(d *decode.D) any { + var riffType string + riffDecode( + d, + nil, + func(d *decode.D, path path) (string, int64) { + id := d.FieldUTF8("id", 4, chunkIDDescriptions) + + const restOfFileLen = 0xffffffff + size := int64(d.FieldScalarUintFn("size", func(d *decode.D) scalar.Uint { + l := d.U32() + if l == restOfFileLen { + return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberHex, Description: "Rest of file"} + } + return scalar.Uint{Actual: l, DisplayFormat: scalar.NumberDecimal} + }).Actual) + + if size == restOfFileLen { + size = d.BitsLeft() / 8 + } + return id, size + }, + func(d *decode.D, id string, path path) (bool, any) { + switch id { + case "FORM": + riffType = d.FieldUTF8("format", 4, d.StrAssert(aiffRiffType)) + return true, nil + case "COMT": + numComments := d.FieldU16("num_comments") + d.FieldArray("comments", func(d *decode.D) { + for i := 0; i < int(numComments); i++ { + d.FieldStruct("comment", func(d *decode.D) { + d.FieldU32("timestamp") + d.FieldU16("marker_id") + count := d.FieldU16("count") + pad := count % 2 + d.FieldUTF8("text", int(count)) + if pad != 0 { + d.FieldRawLen("pad", int64(pad)*8) + } + }) + } + }) + return false, nil + case "COMM": + d.FieldU16("num_channels") + d.FieldU32("num_sample_frames") + d.FieldU16("sample_size") + // TODO: support big float? + d.FieldFltFn("sample_rate", func(d *decode.D) float64 { + return mathex.NewFloat80FromBytes(d.BytesLen(10)).Float64() + }) + return false, nil + case "SSND": + d.FieldU32("offset") + d.FieldU32("block_size") + d.FieldRawLen("data", d.BitsLeft()) + return false, nil + case "MARK": + numMarkers := d.FieldU16("num_markers") + d.FieldArray("markers", func(d *decode.D) { + for i := 0; i < int(numMarkers); i++ { + d.FieldStruct("marker", func(d *decode.D) { + d.FieldU16("id") + d.FieldU32("position") + d.FieldStrFn("name", aiffPString) + }) + } + }) + return false, nil + default: + d.FieldRawLen("data", d.BitsLeft()) + return false, nil + } + }, + ) + + if riffType != aiffRiffType { + d.Errorf("wrong or no AIFF riff type found (%s)", riffType) + } + + return nil +} diff --git a/format/riff/testdata/sox.aiff b/format/riff/testdata/sox.aiff new file mode 100644 index 0000000000000000000000000000000000000000..8179aa4f76754fd923cba6d521e11466b8f25789 GIT binary patch literal 8908 zcmeI0_jeH0x`wCs*_%+MNr-@kCP+IJF-R9M5RfKCAZQSTND-t92qJK#DTs7Liu5K1 zX$nXcP?`n=q)3zOJ-yGJc<$eD*1BG1)|#@`{4n3z-}}Aqep+Q_bs&heBhA{jYSlcm zLl=S|$`Is@=Ra?kJQe9UeAs}2BSs9&PVN7GYUg3ylP{95iJ}Ba5Gjp_A{k&)i{w2y zI(L3Gc}g4qpSQv*NsuuW(hl>Oy}_O4w+VB^{!$I;vHYd-s`5x3uf|{$$cJ^{IJgU5 zgBFaT18>4SI2ASlcfk|@)HzD1^pp3>jMPFLE3D&Ba(CH)m6;NB8R}{BY2xX)d{jP6 z4J!LJyvA;(GsvE0?Kbb5&l ztz~PPKF6qQ{Aqq=4YQj$<($}k;Qi*G4UUILqoeUr;uv|1Izk_0zF{|Vi}(pbFR_k9 zNI%Hql;@NaYH#%xcpV&tsc;C~053ufM$m_U!Xt1Vd=>a$38<=mtyWN`%Qxg2Qm(i` zxXg!qS?&e)Wu^n&h3ZCjC%VVoqHbZ2ptnEJ8}3eXmf8EQyH+W)v+=q9P;aa4)T-kh zxD~pOmZIM1d6a@E#Gn$W0UCrhA|JnwPiU>Qb9#=U8VAg=R)$@~zVDoJcY3S+MZw%~ zb~G!VMa(8=Q?uw9%p`UM*N<;2R23=lXK9h#S)r7#)F$dN&|a z!Oe5d+XZ$>tED-~_})m-hiDhHw)iyejY6~uy^a2bN+wlMs0gZwdZ8ugPuv`D)t=L~ z=`HlT#zON|tEhFu{>EA4PV|QOeS>ac=crTMiO3>5Q61?HOl!6YSB)>hJHpT6m(mb9 zUCvjgsAbjlpbA(7T-Y9d0)I$W!cRI&gO}h&I25LV&ppIx3x`1ooPc>PatEcIQj3MST=51@6J>AK6+j{lv-lg5qyT0SS2m->pI2}k(*Jjaz|t1}Jg z7pV-g8IciZL>b}BK`TGg>*Wq}KC(AhKU=KX%$TX?>y5Opv{bwfzld(4Pf-t49l@lp zSo9QXjE17^n8w-of|jNIu8%g#89$k`tSq~d9XU7L6W+J}=3rg8CR!b@A=Z#<23%x1+d%-*5peQHKA#%t;R1TfZ^k%cTW_(pa5$=k6qzQ6E`JpmbeNNp4YJ-)K zgjsMQ{1M(ys)!(hzreLH2bKi~K^HY&?V=o1%FDUZ9#Io32_5*M+zfUJvyR?M?IL#* zyW`!_u5fp-*FWf;bboV@quO<>e&**!zLBm^(e7!lxjL?8RPcx+W9XA^}D% zGx=YIUE&O>lUzhTq6|@a^;1w1%mj~MGdKh8hqs`cbe0M8;dVG0)&v*9P}NpPD1Ru; z<;BuXDOG$;n9Q%{_Os`h>-0V9FS39rhzp{}VL^a>+b6vuZe^#Loo&rEkD08|QD3J^ z+F0!1Y4~Y$98E@TQAH#sb%@FR6*L8%z^QmT_OuDQs&6zpo1%HbT4?7uE!}jlgwOdw zV24Iz#0FuK28HQI%q{jJcbNZ5m@0OcDoJVlq2)}qi0%b=IgNVFHr_#J#*>!Ur? zKQyWv*Ue?tK)Zoc)G^)b-dX=ha3IW!zKy>n^2j`DFTIP|#IE3G^CN^TFlE)$M8(WsZDLt}neW?htMYJYSh> z!ggSK&~H!!$ZR4z9uN%(vx7l?t~bH`#93$mXcbr$&0fYTUDrEmdD;v38{7{4g;t<9 zQ0+t;g$gO2Xyr|`4%xUXKB8r4C-r^?YwR}PwHnyGeZx8AZtxcQvw~^imWj{f{+L8t(L1OSDwWJDDw$i=QBSl8-NG;7jar(vL2s&GH)fkH zEz!DUZ+7OmBfNfom!MtPI%*lWBwCTJsFrjyrZHQatH?|IU&3*5i8Mg2DF332RZFO= zK}9ehn6M3;4G$+X*GoD}PiF8Y_%5shPJ;pJBlRuif>KxhNIEY8v6(QKpTn(V^O)oG zdFm2*g}4%5jxL8+f_(p`_t5p+B2IO?qcy?YX&6R^zE}&iq4+O67MDba(Kys1@s9s^ zVI}k`nt{&abUasMwV8TZ{aYj3EMw+d>+JDP54WjT)h`zm3qd5u5+RccCDS59GcNm( zyTtDk7KuZo`jRbgQeIOubt+)NSa1V2fD_>!cs=pKLNB}yzk!osL+}Tf07P}VVktf4 zZIUlF7T*kKknJ8b8D@$*QD}q-^@llc+zkshp8EgVCsXwR7W<_NIE#J(w)EHCLZc6)54VxLFz{*OqT7Gu1S83#bm3f)HlH`S2*bm!!og znOhg&Iye|s07pO%^`_cWIi^&W$4CdoK&&ZrU{g2^?o0Mjg=x`(SK&@L7S;y8f)Q${j#cg}?c|lx11VkXBYezn z;(lcFnFq8^Iiyecu^)M%7lZ-jD_$A5mebB2YArF(n!xC;Z_!h<$vDC@aT+>>rlNKz z?SIo^Dr$`;p<}ol{s5cWXr0s780}5cJY;=r_i-Az6+FrFeH=Ut??w0Gd&GV6K6RJA z$y{Mia(not!dNj=Dl1)+7b-6)zpJ^b0S1F#V0AbOZiB!5ofchq8yl2Mi#wBy1)yuBo z2+jldf_KFKHrNqvjkd&Fh%Mw6YBRl_S;;QsKHzhNHevtI8W*+KP8`1MyNnt3d(^w0K=B>V|Xy> zs6sC^;ID8i90{v}^B_mn)OVD8rLjCu`c*0=wiDjt7jZk-qs%Y#Rq7^ri?|)%iv9?1 z1%LXFJj-LX9^fFV-pRHx~a3|BP>!tcBfe=y=5yf%*PbMc~{Dho5At(PQ$;p_1G&%X> y2|0N}PM(mHC* +package mathex + +import ( + "fmt" + "log" + "math" + "math/big" +) + +// Float80 represents an 80-bit IEEE 754 extended precision floating-point +// value, in x86 extended precision format. +// +// References: +// +// https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format +type Float80 struct { + // Sign and exponent. + // + // 1 bit: sign + // 15 bits: exponent + se uint16 + // Integer part and fraction. + // + // 1 bit: integer part + // 63 bits: fraction + m uint64 +} + +// Bits returns the IEEE 754 binary representation of f, with the sign and +// exponent in se and the mantissa in m. +func (f Float80) Bits() (se uint16, m uint64) { + return f.se, f.m +} + +// Bytes returns the x86 extended precision binary representation of f as a byte +// slice. +func (f Float80) Bytes() []byte { + return []byte(f.String()) +} + +// String returns the IEEE 754 binary representation of f as a string, +// containing 10 bytes in hexadecimal format. +func (f Float80) String() string { + return fmt.Sprintf("%04X%016X", f.se, f.m) +} + +// Float64 returns the float64 representation of f. +func (f Float80) Float64() float64 { + se := uint64(f.se) + m := f.m + // 1 bit: sign + sign := se >> 15 + // 15 bits: exponent + exp := se & 0x7FFF + // Adjust for exponent bias. + // + // === [ binary64 ] ========================================================= + // + // Exponent bias 1023. + // + // +===========================+=======================+ + // | Exponent (in binary) | Notes | + // +===========================+=======================+ + // | 00000000000 | zero/subnormal number | + // +---------------------------+-----------------------+ + // | 00000000001 - 11111111110 | normalized value | + // +---------------------------+-----------------------+ + // | 11111111111 | infinity/NaN | + // +---------------------------+-----------------------+ + // + // References: + // https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Exponent_encoding + exp64 := int64(exp) - 16383 + 1023 + switch { + case exp == 0: + // exponent is all zeroes. + exp64 = 0 + case exp == 0x7FFF: + // exponent is all ones. + exp64 = 0x7FF + default: + } + // 63 bits: fraction + frac := m & 0x7FFFFFFFFFFFFFFF + // Sign, exponent and fraction of binary64. + // + // 1 bit: sign + // 11 bits: exponent + // 52 bits: fraction + // + // References: + // https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64 + bits := sign<<63 | uint64(exp64)<<52 | frac>>11 + return math.Float64frombits(bits) +} + +// BigFloat returns the *big.Float representation of f. +func (f Float80) BigFloat() *big.Float { + x := &big.Float{} + sign := (f.se & 0x8000) != 0 + e := f.se & 0x7FFF + s := fmt.Sprintf("0x.%Xp%d", f.m, e-16383+1) + if sign { + s = "-" + s + } + x.SetPrec(52) + _, _, err := x.Parse(s, 0) + if err != nil { + log.Printf("big.Float.Parse: error %v", err) + } + return x +} + +// NewFloat80FromFloat64 returns the nearest 80-bit floating-point value for x. +func NewFloat80FromFloat64(x float64) Float80 { + // Sign, exponent and fraction of binary64. + // + // 1 bit: sign + // 11 bits: exponent + // 52 bits: fraction + bits := math.Float64bits(x) + // 1 bit: sign + sign := uint16(bits >> 63) + // 11 bits: exponent + exp := bits >> 52 & 0x7FF + // 52 bits: fraction + frac := bits & 0xFFFFFFFFFFFFF + + if exp == 0 && frac == 0 { + // zero value. + return Float80{} + } + + // Sign, exponent and fraction of binary80. + // + // 1 bit: sign + // 15 bits: exponent + // 1 bit: integer part + // 63 bits: fraction + + // 15 bits: exponent. + // + // Exponent bias 1023 (binary64) + // Exponent bias 16383 (binary80) + exp80 := int64(exp) - 1023 + 16383 + // 63 bits: fraction. + // + frac80 := frac << 11 + switch { + case exp == 0: + exp80 = 0 + case exp == 0x7FF: + exp80 = 0x7FFF + } + se := sign<<15 | uint16(exp80) + // Integer part set to specify normalized value. + m := 0x8000000000000000 | frac80 + return NewFloat80FromBits(se, m) +} + +// NewFloat80FromBytes returns a new 80-bit floating-point value based on b, +func NewFloat80FromBytes(b []byte) Float80 { + var f Float80 + if len(b) != 10 { + panic(fmt.Errorf("invalid length of float80 representation, expected 10, got %d", len(b))) + } + f.se = uint16(int64(b[0])<<8 | int64(b[1]<<0)) + f.m = uint64(0 | + int64(b[2])<<56 | + int64(b[3])<<48 | + int64(b[4])<<40 | + int64(b[5])<<32 | + int64(b[6])<<24 | + int64(b[7])<<16 | + int64(b[8])<<8 | + int64(b[9])<<0, + ) + return f +} + +// NewFloat80FromBits returns a new 80-bit floating-point value based on the +// sign, exponent and mantissa bits. +func NewFloat80FromBits(se uint16, m uint64) Float80 { + return Float80{ + se: se, + m: m, + } +}