From: Charlie Savage Date: 2006-06-10T08:55:29+09:00 Subject: Re: Converting a Float back to bytes --------------ms010200080006080500070709 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Here are two functions in javascript that convert a double to and from a hex encoded byte stream. They are part of a larger class but should more or less make sense. Some notes: * this is for doubles, ie 64-bit values, that are IEEE 754 encoded * this.hex is a hex string * WKB.WKB_XDR is big endian * WKB.WKB_NDR is little endian There is a good article on wikipedia about this: http://en.wikipedia.org/wiki/IEEE_754 Charlie --------------------- readDouble: function() { // Read IEEE 754 64-bit (8 bytes) double var doubleHex = this.hex.slice(this.pos, this.pos + 16) this.pos += 16 if (this.byteOrder == Geometry.WKB.WKB_NDR) doubleHex = Geometry.WKB.swapEndian(doubleHex) // get first 12 bits var signExponentHex = doubleHex.slice(0, 3) var signExponent = parseInt(signExponentHex, 16) // get sign and exponent var sign = signExponent & 0x800 var exponent = signExponent & 0x7FF // get fraction which is 52 bits var fractionHex = doubleHex.slice(3, 16) var fraction = parseInt(fractionHex, 16) if (exponent == 2047 && fraction != 0) { // NaN - Not a number value = Number.NaN } else if (exponent == 2047 && fraction == 0 && sign == 1) { // Negative infinity value = Number.NEGATIVE_INFINITY } else if (exponent == 2047 && fraction == 0 && sign == 0) { // Positive infinity value = Number.POSITIVE_INFINITY } else if (exponent > 0 && exponent < 2047) { // Most significant byte should be 1 in normalized form fraction += Math.pow(2,52) value = fraction * Math.pow(2, exponent - 1023 - 52) if (sign) value = -value } else if (exponent == 0 && fraction != 0) { // Denormal number value = fraction * Math.pow(2, -1022 - 52); if (sign) value = -value } else if (exponent == 0 && fraction == 0 && sign == 1) { // Negative zero value = 0 } else if (exponent == 0 && fraction == 0 && sign == 0) { // Positive zero value = 0 } else { throw new Error('Could not parse floating point number') } return value } writeDouble: function(value) { // get integer and fraction var positiveValue = Math.abs(value) var integer = Math.floor(positiveValue) var fraction = positiveValue - integer var intBin = integer.toString(2) // convert to fraction, then remove leading "0." var fractionBin = fraction.toString(2).slice(2) var exponent = null var significandBin = null if (integer >= 1) { // Figure out exponent - 1023 is the bias bias exponent = 1023 + intBin.length - 1 significandBin = intBin.slice(1) + fractionBin } else { var index = fractionBin.search(/1/) if (index == -1) { exponent = 0 significandBin = "0" } else { // figure out exponent - 1023 is the bias bias exponent = 1023 - index - 1 significandBin = fractionBin.slice(index + 1) } } // write sign bit if (value < 0) bin = "1" else bin = "0" // write exponent which is 11 bits bin += this.padLeft(exponent.toString(2), 11) // write the significand which is 52 bits bin += this.padRight(significandBin, 52) // Now convert to hex one byte at a time var value = "" for (var i=0; i<8; i++) { var temp = parseInt(bin.slice(i*8, i*8+8),2) // Geos only accepts hex values in upper case var upper = temp.toString(16).toUpperCase() value += this.padLeft(upper.toString(16),2) } if (this.byteOrder == Geometry.WKB.WKB_NDR) value = Geometry.WKB.swapEndian(value) this.hex += value } Harris Reynolds wrote: > This is good stuff. I am now getting the correct Float object (21.0) from a string of bytes ("@5\000\000\000\000\000\000"). Now... here is a question... is there an easy way to go from the Float back to bytes. This seems like it may be much more difficult. > > something like this... > > float_object = 21.0 > bytes = SOME_API_HERE.convert(float_object) > > I think a result of this work I am doing should be a general purpose BitConverter class similar to what .Net offers. This would make working with binary data in Ruby much less painful. > > thanks for any help, > > ~harris > > p.s. Mike... thanks a bunch for the help on going from Bytes to a Float!!! I owe you... are you going to be at RailsConf? > > > > ----- Original Message ---- > From: Mike Stok > To: ruby-talk ML > Sent: Thursday, June 8, 2006 10:49:36 PM > Subject: Re: Converting 8 bytes to a Float > > > On 8-Jun-06, at 10:39 PM, Harris Reynolds wrote: > >> Mike... thanks for the tip. My mileage is varying though... here >> is the same irb session on my machine: >> >> irb(main):004:0> s = "" >> => "" >> irb(main):005:0> s << 64 << 53 << "\0" * 6 >> => "@5\000\000\000\000\000\000" >> irb(main):006:0> s.unpack('D') >> => [6.73510288410787e-320] >> >> I'm not getting the clean result for some reason. >> > > I'm on a PowerPC, and you're probably on an x86. Our endian-ness is > different. > > D uses native format to unpack the bytes, if your bytes are laid out > the way you said they are then you probably want to use G which is > specifically "big endian". > > See http://en.wikipedia.org/wiki/Endian for more background. > > Hope this helps, > > Mike > --------------ms010200080006080500070709 Content-Type: application/x-pkcs7-signature; name="smime.p7s" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="smime.p7s" Content-Description: S/MIME Cryptographic Signature MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIJAzCC AtwwggJFoAMCAQICEFby1ChIT+pmYLkFBWX20TIwDQYJKoZIhvcNAQEEBQAwYjELMAkGA1UE BhMCWkExJTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5nIChQdHkpIEx0ZC4xLDAqBgNVBAMT I1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBJc3N1aW5nIENBMB4XDTA2MDUxMzIyNTQyM1oX DTA3MDUxMzIyNTQyM1owQzEfMB0GA1UEAxMWVGhhd3RlIEZyZWVtYWlsIE1lbWJlcjEgMB4G CSqGSIb3DQEJARYRY2Zpc0BzYXZhZ2V4aS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDDPyi9Z2s8673fAOxX6/EhWhAo2bb8/BcU7xiV49a9TWExEAufzQuAKvKdRWlm MKuTwdU/QKgrZ1bCcb3cIznHjFI5AmG4nkqUMzqzgzPDDwbiE6AXYXzdd+efDF1mVUQJF/nm N26xC576KC3e+T1jK2n4DosnIxqQlTVH02huREgMrnPDR82susVvtKa3BV1xx1Fm1+8qkISp 4Ym8LaBQ72nH879OFLHeH/1oP4FAp7qfZAWtlse8mSajhHN6mAH+wFUOG1yrNsM2mKVVAhIv ByolY+VTa9RBWXgvOhdmJyQd56fag85/zaUjQfUChiYBmx4IWVbc9TKtcx6btDxrAgMBAAGj LjAsMBwGA1UdEQQVMBOBEWNmaXNAc2F2YWdleGkuY29tMAwGA1UdEwEB/wQCMAAwDQYJKoZI hvcNAQEEBQADgYEAeCTsNZ/SvrF8JPbFBychryTZlDzu415g2JQDaPa9+gBFaMpWvsE/rzNO 2nBhz4nXayb8QQMA5+RjTavo/fEmittd2ZjNYItA7glkw0R01kmf/ZEWehLQrVoQ5bbKycED giA0ykm/936Gzin+RrXwYc9qp5CGawWFj7ceOqmUv8owggLcMIICRaADAgECAhBW8tQoSE/q ZmC5BQVl9tEyMA0GCSqGSIb3DQEBBAUAMGIxCzAJBgNVBAYTAlpBMSUwIwYDVQQKExxUaGF3 dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMSwwKgYDVQQDEyNUaGF3dGUgUGVyc29uYWwgRnJl ZW1haWwgSXNzdWluZyBDQTAeFw0wNjA1MTMyMjU0MjNaFw0wNzA1MTMyMjU0MjNaMEMxHzAd BgNVBAMTFlRoYXd0ZSBGcmVlbWFpbCBNZW1iZXIxIDAeBgkqhkiG9w0BCQEWEWNmaXNAc2F2 YWdleGkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwz8ovWdrPOu93wDs V+vxIVoQKNm2/PwXFO8YlePWvU1hMRALn80LgCrynUVpZjCrk8HVP0CoK2dWwnG93CM5x4xS OQJhuJ5KlDM6s4Mzww8G4hOgF2F83XfnnwxdZlVECRf55jdusQue+igt3vk9Yytp+A6LJyMa kJU1R9NobkRIDK5zw0fNrLrFb7SmtwVdccdRZtfvKpCEqeGJvC2gUO9px/O/ThSx3h/9aD+B QKe6n2QFrZbHvJkmo4RzepgB/sBVDhtcqzbDNpilVQISLwcqJWPlU2vUQVl4LzoXZickHeen 2oPOf82lI0H1AoYmAZseCFlW3PUyrXMem7Q8awIDAQABoy4wLDAcBgNVHREEFTATgRFjZmlz QHNhdmFnZXhpLmNvbTAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBAUAA4GBAHgk7DWf0r6x fCT2xQcnIa8k2ZQ87uNeYNiUA2j2vfoARWjKVr7BP68zTtpwYc+J12sm/EEDAOfkY02r6P3x JorbXdmYzWCLQO4JZMNEdNZJn/2RFnoS0K1aEOW2ysnBA4IgNMpJv/d+hs4p/ka18GHPaqeQ hmsFhY+3HjqplL/KMIIDPzCCAqigAwIBAgIBDTANBgkqhkiG9w0BAQUFADCB0TELMAkGA1UE BhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl cyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVtYWlsIENBMSswKQYJ KoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUuY29tMB4XDTAzMDcxNzAwMDAw MFoXDTEzMDcxNjIzNTk1OVowYjELMAkGA1UEBhMCWkExJTAjBgNVBAoTHFRoYXd0ZSBDb25z dWx0aW5nIChQdHkpIEx0ZC4xLDAqBgNVBAMTI1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBJ c3N1aW5nIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEpjxVc1X7TrnKmVoeaMB1 BHCd3+n/ox7svc31W/Iadr1/DDph8r9RzgHU5VAKMNcCY1osiRVwjt3J8CuFWqo/cVbLrzwL B+fxH5E2JCoTzyvV84J3PQO+K/67GD4Hv0CAAmTXp6a7n2XRxSpUhQ9IBH+nttE8YQRAHmQZ cmC3+wIDAQABo4GUMIGRMBIGA1UdEwEB/wQIMAYBAf8CAQAwQwYDVR0fBDwwOjA4oDagNIYy aHR0cDovL2NybC50aGF3dGUuY29tL1RoYXd0ZVBlcnNvbmFsRnJlZW1haWxDQS5jcmwwCwYD VR0PBAQDAgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFQcml2YXRlTGFiZWwyLTEzODAN BgkqhkiG9w0BAQUFAAOBgQBIjNFQg+oLLswNo2asZw9/r6y+whehQ5aUnX9MIbj4Nh+qLZ82 L8D0HFAgk3A8/a3hYWLD2ToZfoSxmRsAxRoLgnSeJVCUYsfbJ3FXJY3dqZw5jowgT2Vfldr3 94fWxghOrvbqNOUQGls1TXfjViF4gtwhGTXeJLHTHUb/XV9lTzGCA2QwggNgAgEBMHYwYjEL MAkGA1UEBhMCWkExJTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5nIChQdHkpIEx0ZC4xLDAq BgNVBAMTI1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBJc3N1aW5nIENBAhBW8tQoSE/qZmC5 BQVl9tEyMAkGBSsOAwIaBQCgggHDMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZI hvcNAQkFMQ8XDTA2MDYwOTIzNTMzNFowIwYJKoZIhvcNAQkEMRYEFPWbq6CLITaHKdhJsRnF 3Y6vvidrMFIGCSqGSIb3DQEJDzFFMEMwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0G CCqGSIb3DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMIGFBgkrBgEEAYI3EAQxeDB2 MGIxCzAJBgNVBAYTAlpBMSUwIwYDVQQKExxUaGF3dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQu MSwwKgYDVQQDEyNUaGF3dGUgUGVyc29uYWwgRnJlZW1haWwgSXNzdWluZyBDQQIQVvLUKEhP 6mZguQUFZfbRMjCBhwYLKoZIhvcNAQkQAgsxeKB2MGIxCzAJBgNVBAYTAlpBMSUwIwYDVQQK ExxUaGF3dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMSwwKgYDVQQDEyNUaGF3dGUgUGVyc29u YWwgRnJlZW1haWwgSXNzdWluZyBDQQIQVvLUKEhP6mZguQUFZfbRMjANBgkqhkiG9w0BAQEF AASCAQCKfm4MEukcuubrCGYDMzabRy30pdxZ3ZKoXqCmfjJXRIpdQT4j3PcKy5wxQ81YOxaO 4AAJicCxtRb6KlhNmMgAdT2QHe13eHuSMq+W4DiKVtwLuP6I8tZzJ3dGDwmdrA/l90Dxyoii 7RpwaLjCFiXHY3OI9uv1Xh31dlfpZuz6wkCy+iGlEOVARIzBdhOH0J7XWe+/v89+vreZ9Db5 vUCaJzHrkrep1mmnNe85xw1MEhZNMq5OFsrPbyVkxh/z50zwi1riG91oIA+ezXL66sd8J2p/ SWsb907X09tvP1AuR/b5lNlbFbgpXDQLUHImM6/U4bRFDMMoksLFIpyh3g5hAAAAAAAA --------------ms010200080006080500070709--