Decimal floating point number to binary: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Fortran}}: Whoops, forgot to show both bases...)
Line 79: Line 79:
This is a cut-back version of a free-format routine EATREAL that worked in a more general context. The text was to be found in an external variable ACARD with external fingers L1 and L2; L1 marked the start point and L2 advanced through the number. If a problem arose then an error message could denounce the offending text ACARD(L1:L2) as well as just say "Invalid input" or similar. The routine worked in base ten only, but it is a trivial matter to replace *10 by *BASE. Here however a possible base may extend beyond just the decimal digits, so it is no longer possible to rely on zero to nine only and their associated character codes, thus the "digit" is now identified by indexing into an array of digits, thereby enabling "A" to follow "9" without character code testing. For handling the rescaling needed for fractional digits, a table of powers of ten up to sixteen was defined, but now the base may not be ten so BASE**DD is computed on the fly. The original routine was intended for usages in the hundreds of millions of calls, so this version would be unsuitable for that! Further, the exponent addendum (as in 35E+16) can no longer be recognised because "E" is now a possible digit. In handling this, the exponent part was added to DD and who knows, the result may produce a zero DD as in "123.456E-3" but otherwise a MOD(DD,16) would select from the table of powers of ten, and beyond that would be handled by successive squaring. Because on a binary computer most decimal fractions are recurring sequences of binary digits, it is better to divide by ten than to multiply by 0·1.
This is a cut-back version of a free-format routine EATREAL that worked in a more general context. The text was to be found in an external variable ACARD with external fingers L1 and L2; L1 marked the start point and L2 advanced through the number. If a problem arose then an error message could denounce the offending text ACARD(L1:L2) as well as just say "Invalid input" or similar. The routine worked in base ten only, but it is a trivial matter to replace *10 by *BASE. Here however a possible base may extend beyond just the decimal digits, so it is no longer possible to rely on zero to nine only and their associated character codes, thus the "digit" is now identified by indexing into an array of digits, thereby enabling "A" to follow "9" without character code testing. For handling the rescaling needed for fractional digits, a table of powers of ten up to sixteen was defined, but now the base may not be ten so BASE**DD is computed on the fly. The original routine was intended for usages in the hundreds of millions of calls, so this version would be unsuitable for that! Further, the exponent addendum (as in 35E+16) can no longer be recognised because "E" is now a possible digit. In handling this, the exponent part was added to DD and who knows, the result may produce a zero DD as in "123.456E-3" but otherwise a MOD(DD,16) would select from the table of powers of ten, and beyond that would be handled by successive squaring. Because on a binary computer most decimal fractions are recurring sequences of binary digits, it is better to divide by ten than to multiply by 0·1.


The source is F77 style, except for the MODULE usage simply for some slight convenience in sharing DIGIT. <lang Fortran> MODULE REBASE !Play with some conversions between bases.
The source is F77 style, except for the MODULE usage simply for some slight convenience in sharing DIGIT. The codes for hexadecimal, octal and binary formats do ''not'' read or write numbers in those bases, they show the bit pattern of the numerical storage format instead, and for floating-point numbers this is very different. <lang Fortran> MODULE REBASE !Play with some conversions between bases.
CHARACTER*36 DIGIT !A set of acceptable digit characters.
CHARACTER*36 DIGIT !A set of acceptable digit characters.
PARAMETER (DIGIT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") !Not only including hexadecimal.
PARAMETER (DIGIT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") !Not only including hexadecimal.

Revision as of 12:49, 5 July 2016

Decimal floating point number to binary is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Your task is to create a program that takes a decimal floating point number and displays its binary representation and vice-versa: takes a floating point binary number and outputs its decimal representation.

The output might be something like this:

23.34375 => 10111.01011
1011.11101 => 11.90625

D

Translation of: Python

<lang d>import std.stdio, std.conv, std.array, std.string, std.range, std.algorithm, std.typecons;

immutable string[string] hex2bin, bin2hex;

static this() pure @safe {

   hex2bin = 16.iota.map!(x => tuple("%x".format(x), "%04b".format(x))).assocArray;
   bin2hex = 16.iota.map!(x => tuple("%b".format(x), "%x".format(x))).assocArray;

}

string dec2bin(real d) pure @safe {

   immutable neg = d < 0;
   if (neg)
       d = -d;
   immutable hx = "%a".format(d);
   immutable p = hx.countUntil('p');
   immutable bn = hx[2 .. p].split("").map!(ch => hex2bin.get(ch, ch)).join;
   // Currently Phobos lacks a strip(string, string) function.
   // return (neg ? "-" : "") ~ bn.strip("0")
   return (neg ? "-" : "") ~ bn.tr("0", " ").strip.tr(" ", "0")
          ~ hx[p .. p + 2] ~ "%b".format(hx[p + 2 .. $].to!int);

}

real bin2dec(string bn) pure /*@safe*/ in {

   assert(!bn.empty);

} body {

   immutable neg = bn[0] == '-';
   if (neg)
       bn = bn[1 .. $];
   immutable dp1 = bn.countUntil('.');
   immutable extra0a = "0".replicate(4 - dp1 % 4);
   immutable bn2 = extra0a ~ bn;
   immutable dp2 = bn2.countUntil('.');
   immutable p = bn2.countUntil('p');
   auto hx = iota(0, dp2 + 1, 4)
             .map!(i => bin2hex.get(bn2[i..min(i + 4, p)]
                                    .tr("0", " ")
                                    .stripLeft
                                    .tr(" ", "0"),
                                    bn2[i .. i + 1]))
             .join;
   immutable bn3 = bn2[dp2 + 1 .. p];
   immutable extra0b = "0".replicate(4 - bn3.length % 4);
   immutable bn4 = bn3 ~ extra0b;
   hx ~= iota(0, bn4.length, 4)
         .map!(i => bin2hex[bn4[i .. i + 4].tr("0", " ").stripLeft.tr(" ", "0")])
         .join;
   hx = (neg ? "-" : "") ~ "0x" ~ hx ~ bn2[p .. p+2] ~ bn2[p + 2 .. $].to!int(2).text;
   return hx.to!real;

}

void main() /*@safe*/ {

   immutable x = 23.34375;
   immutable y1 = x.dec2bin;
   y1.writeln;
   writefln("%.6f", y1.bin2dec);
   immutable y2 = dec2bin(-x);
   y2.writeln;
   y2.bin2dec.writeln;
   writefln("%.6f", "1011.11101p+0".bin2dec);

}</lang>

Output:
1.011101011p+100
23.343750
-1.011101011p+100
-23.3438
11.906250

Fortran

This is a cut-back version of a free-format routine EATREAL that worked in a more general context. The text was to be found in an external variable ACARD with external fingers L1 and L2; L1 marked the start point and L2 advanced through the number. If a problem arose then an error message could denounce the offending text ACARD(L1:L2) as well as just say "Invalid input" or similar. The routine worked in base ten only, but it is a trivial matter to replace *10 by *BASE. Here however a possible base may extend beyond just the decimal digits, so it is no longer possible to rely on zero to nine only and their associated character codes, thus the "digit" is now identified by indexing into an array of digits, thereby enabling "A" to follow "9" without character code testing. For handling the rescaling needed for fractional digits, a table of powers of ten up to sixteen was defined, but now the base may not be ten so BASE**DD is computed on the fly. The original routine was intended for usages in the hundreds of millions of calls, so this version would be unsuitable for that! Further, the exponent addendum (as in 35E+16) can no longer be recognised because "E" is now a possible digit. In handling this, the exponent part was added to DD and who knows, the result may produce a zero DD as in "123.456E-3" but otherwise a MOD(DD,16) would select from the table of powers of ten, and beyond that would be handled by successive squaring. Because on a binary computer most decimal fractions are recurring sequences of binary digits, it is better to divide by ten than to multiply by 0·1.

The source is F77 style, except for the MODULE usage simply for some slight convenience in sharing DIGIT. The codes for hexadecimal, octal and binary formats do not read or write numbers in those bases, they show the bit pattern of the numerical storage format instead, and for floating-point numbers this is very different. <lang Fortran> MODULE REBASE !Play with some conversions between bases.

      CHARACTER*36 DIGIT	!A set of acceptable digit characters.
      PARAMETER (DIGIT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")	!Not only including hexadecimal.
      CONTAINS		!No great complication.
       LOGICAL FUNCTION EATNUM(ACARD,BASE,V)	!Reads a text number using the specified base.

Chews into the likes of 666, -666.666, .666 with their variations. Completes with the value in V, success as the result. Could check that no digit exceeds the specified BASE usage, but that would mean an error message... Concocted by R.N.McLean (whom God preserve) May XXMM. Clunky usage of ICHAR encourages the compaq compiler to employ single-character-at-a-time usage.

        CHARACTER*(*) ACARD	!The text.
        INTEGER BASE		!The base may not be ten.
        DOUBLE PRECISION V	!The object of the exercise.
        DOUBLE PRECISION X	!Maximum precision for all this fun. No sign of REAL*10.
        INTEGER D,DD		!A single digit. and a digit count.
        INTEGER L2,LC		!Finger and limit.
        INTEGER*1 C		!Since ICHAR is in use.
        LOGICAL ADIGIT,XNEG	!Things noticed along the way.
         ADIGIT = .FALSE.	!No digits seen.
         XNEG = .FALSE.	!No negative number.
         DD = 0		!No decimal digits.
         X = 0			!No value.
         L2 = 1		!The starting point.
         LC = LEN(ACARD)	!The ending point
         IF (L2.GT.LC) GO TO 20	!Urk! Off the end even before I start.

Chew into the number. Admit a possible leading sign, then digits.

         C = ICHAR(ACARD(L2:L2))	!Grab, since there are two comparisons.
         IF (C.EQ.ICHAR("+")) GO TO 1	!First consider
         IF (C.NE.ICHAR("-")) GO TO 2	!Possible signs.
         XNEG = .TRUE.		!To be acted upon later.
   1     L2 = L2 + 1		!Advance one.
         IF (L2.GT.LC) GO TO 20	!Off the end?
   2     D = INDEX(DIGIT,ACARD(L2:L2)) - 1	!No. Taste the candidate digit.
         IF (D .LT. 0) GO TO 10	!Is it to my taste?
         X = D				!Yes. Yum.
         ADIGIT = .TRUE.		!A digit has been seen. One is enough to be seen.
   3     L2 = L2 + 1			!More may follow.
         IF (L2.GT.LC) GO TO 20	!Perhaps not.
         D = ICHAR(ACARD(L2:L2)) - ICHAR("0")	!Taste the candidate digit.
         IF (D .LT. 0) GO TO 10	!Digitish?
         X = X*BASE + D		!Yes. Assimilate.
         GO TO 3			!Even more might well follow.

Consider any decimal digits, introduced by a decimal point.

  10     IF (ICHAR(ACARD(L2:L2)).EQ.ICHAR(".")) GO TO 11	!A full stop as a decimal point?
         IF (ICHAR(ACARD(L2:L2)).NE.ICHAR("·")) GO TO 20	!So, is there a decimal point?
  11     L2 = L2 + 1			!Advance one.
         IF (L2.GT.LC) GO TO 20	!Sudden end?
         D = INDEX(DIGIT,ACARD(L2:L2)) - 1	!No. Taste the digit candidate.
         IF (D .LT. 0) GO TO 20	!Suitable?
         X = X*BASE + D		!Yes. Continue augmenting the number.
         DD = 1			!This is the first decimal digit.
         ADIGIT = .TRUE.		!There may have been none before the decimal point.
  12     L2 = L2 + 1			!If once one digit is seen, is not the jungle full of digits?
         IF (L2.GT.LC) GO TO 20	!Perhaps not.
         D = ICHAR(ACARD(L2:L2)) - ICHAR("0")	!Taste the digit candidate.
         IF (D < 0 .OR. 9 < D) GO TO 20	!Suitable?
         X = X*BASE + D			!Yes. Accept as before.
         DD = DD + 1			!Now, no need to set ADIGIT to true again.
         GO TO 12			!Carry on.

Can't consider any exponent part, started by an "E" or "D", as these may be possible digit symbols.

  20     IF (DD .GT. 0) X = X/BASE**DD	!Rescale for the fractional digits.
         IF (XNEG) X = -X		!Fix the sign.
         V = X				!Place the result.
         EATNUM = ADIGIT		!Report success.
       END FUNCTION EATNUM	!And awayt.
       SUBROUTINE FP8DIGITS(X,BASE,TEXT,L)	!Full expansion of the value of X in BASE.

Converts a number X to a specified BASE. For integers, successive division by BASE, for fractions, successive multiplication.

        REAL*8 X,T		!The value, and an associate.
        INTEGER BASE		!As desired.
        CHARACTER*(*) TEXT	!Scratchpad for results.
        INTEGER L		!The length of the result.
        INTEGER N,ND		!Counters.
        INTEGER D		!The digit of the moment.
        LOGICAL NEG		!Annoyance with signs.
         IF (BASE.LE.1 .OR. BASE.GT.LEN(DIGIT)) BASE = 10	!Preclude oddities.
         WRITE (TEXT,1) BASE	!Scrub the TEXT with an announcement.
   1     FORMAT ("Base",I3)	!A limited range is expected..
         T = X			!Grab the value.
         N = T			!Its integer part, with truncation.
         T = ABS(T - N)	!Thus obtain the fractional part.
         NEG = N .LT. 0	!Negative numbers are a nuisance.
         IF (NEG) N = -N	!So simplify for what follows.
         L = LEN(TEXT)		!Limit of the scratchpad.
         ND = 0		!No digits have been rolled.

Crunch the integer part. Use the tail end of TEXT as a scratchpad, as the size of N is unassessed.

  10     D = MOD(N,BASE)		!Extract the low-order digit in BASE.
         TEXT(L:L) = DIGIT(D+1:D+1)	!Place it as text.
         ND = ND + 1			!Count another digit rolled.
         N = N/BASE			!Drop down a power.
         L = L - 1			!Move back correspondingly.
         IF (L.LE.0) THEN		!Run out of space?
           TEXT = "Overflow!"		!Then, this will have to do!
           L = MIN(9,LEN(TEXT))	!TEXT might be far too short.
          RETURN			!Give up.
         END IF			!But, space is expected.
         IF (N.GT.0) GO TO 10		!Are we there yet?
         IF (NEG) THEN			!Yes! Is a negative sign needed?
           TEXT(L:L) = "-"		!Yes. Place it.
           L = L - 1			!And retreat another place.
         END IF			!No + sign for positive numbers.
         N = LEN(TEXT) - L		!So, how much scratchpad was used?
         TEXT(9:9 + N - 1) = TEXT(L + 1:)	!Append to the initial TEXT(1:8) from the start.
         L = 9 + N			!Finger what follows the units position.
         TEXT(L:L) = "."		!Laziness leads to a full stop for a decimal point.

Crunch through the fractional part until nothing remains.

         DO WHILE(T.GT.0)	!Eventually, this will be zero.
           IF (L.GE.LEN(TEXT)) THEN	!Provided I have enough space!
             L = LEN(TEXT)		!If not, use the whole supply.
             TEXT(L:L) = "~"		!Place a marker suggesting that more should follow.
            RETURN		!And give up.
           END IF		!Otherwise, a digit is to be found.
           T = T*BASE		!Shift up a power.
           N = T		!The integer part is the digit.
           T = T - N		!Remove that integer part from T.
           L = L + 1		!Advance the finger.
           TEXT(L:L) = DIGIT(N+1:N+1)	!Place the digit.
           ND = ND + 1		!Count it also.
         END DO		!And see if anything remains.

Cast forth an addendum, to save the reader from mumbling while counting long strings of digits.

         IF (LEN(TEXT) - L .GT. 11) THEN	!Err, is there space for an addendum?
           WRITE (TEXT(L + 2:),11) ND		!Yes! Reveal the number of digits.
  11       FORMAT ("Digits:",I3)		!I expect no ore than two-digit digit counts.
           L = L + 1 + 10			!So this should do.
         END IF				!So much for the addendum.
       END SUBROUTINE FP8DIGITS	!Bases play best with related bases, such as 4 and 8. Less so with (say) 3 and 7...
     END MODULE REBASE	!Enough for inspection.
     PROGRAM TESTSOME

Check some conversions from one base to another.

     USE REBASE
     INTEGER N		!Some number of tests.
     PARAMETER (N = 5)		!This number.
     CHARACTER*12 TEXT(N)	!Sufficient size texts.
     DATA TEXT/"23.34375","10111.01011","1011.1101","11.90625","-666"/	!Also demonstrate a negative.
     DOUBLE PRECISION V	!The value in the computer's own representation.
     INTEGER I,L,BASE		!Assistants.
     CHARACTER*88 BACK		!A scratchpad.
     WRITE (6,1)	!A heading would be nice.
   1 FORMAT ("Test text in base",3X,"Value in base 10")

Chug through the tests.

     DO BASE = 10,2,-8	!Odd loop generates BASE = 10 then BASE = 2.
       DO I = 1,N		!Step through the test texts.
         WRITE (6,11) TEXT(I),BASE	!Start the line with the input.
  11     FORMAT (A,I5,$)		!The $, obviously, means no new line.
         IF (.NOT.EATNUM(TEXT(I),BASE,V)) THEN	!There shouldn't be any trouble.
           WRITE (6,*) "Not a good number!"		!But...
          ELSE				!So then,
           WRITE (BACK,*) V			!Reveal the resulting value.
           WRITE (6,12) BACK(1:20)		!Sufficient space, I hope.
  12       FORMAT (A,$)			!All to produce tabular output.
           CALL FP8DIGITS(V,2,BACK,L)		!Convert back to a text string.
           WRITE (6,13) BACK(1:L)		!And reveal.
  13       FORMAT (A)				!Thus end the line.
           CALL FP8DIGITS(V,10,BACK,L)		!And in another base,
           WRITE (6,14) BACK(1:L)		!The same value.
  14       FORMAT (37X,A)			!Nicely aligned.
         END IF			!So much for that test.
       END DO			!On to the next test.
     END DO		!And another base.
     END	!Enough of that. </lang>

Rather than mess about with invocations, the test interprets the texts firstly as base ten sequences, then base two. It makes no complaint over encountering the likes of "666" when commanded to absorb according to base two. The placewise notation is straightforward: 666 = 6x22 + 6x21 + 6x20

Test text in base   Value in base 10
23.34375       10   23.3437500000000 Base  2 10111.01011 Digits: 10
                                     Base 10 23.34375 Digits:  7
10111.01011    10   10111.0101100000 Base  2 10011101111111.00000010100101101001000110100111010111 Digits: 52
                                     Base 10 10111.01010999999925843439996242523193359375 Digits: 43
1011.1101      10   1011.11010000000 Base  2 1111110011.0001110000101111100000110111101101001010001 Digits: 53
                                     Base 10 1011.1100999999999885403667576611042022705078125 Digits: 47
11.90625       10   11.9062500000000 Base  2 1011.11101 Digits:  9
                                     Base 10 11.90625 Digits:  7
-666           10  -666.000000000000 Base  2 -1010011010. Digits: 10
                                     Base 10 -666. Digits:  3
23.34375        2   10.4687500000000 Base  2 1010.01111 Digits:  9
                                     Base 10 10.46875 Digits:  7
10111.01011     2   23.3437500000000 Base  2 10111.01011 Digits: 10
                                     Base 10 23.34375 Digits:  7
1011.1101       2   11.8125000000000 Base  2 1011.1101 Digits:  8
                                     Base 10 11.8125 Digits:  6
11.90625        2   8.53125000000000 Base  2 1000.10001 Digits:  9
                                     Base 10 8.53125 Digits:  6
-666            2  -42.0000000000000 Base  2 -101010. Digits:  6
                                     Base 10 -42. Digits:  2

Note again that a decimal value in binary is almost always a recurring sequence and that the exact decimal value of the actual binary sequence in the computer (of finite length) is not the same as the original decimal value. 23·34375 happens to be an exact decimal representation of a binary value whose digit count is less than that of a floating-point variable. But although 1011·1101 has few digits, in decimal it converts to a recurring sequence in binary just as does 0·1.

Haskell

float to binary part only: <lang haskell>import Data.Char (intToDigit) import Numeric (floatToDigits, showIntAtBase)

dec2bin :: RealFloat a => a -> String dec2bin f = "0." ++ map intToDigit digits ++ "p+" ++ showIntAtBase 2 intToDigit ex ""

 where (digits, ex) = floatToDigits 2 f

main :: IO () main = putStrLn $ dec2bin 23.34375</lang>

Output:
0.1011101011p+101

J

In this draft, the task does not give any guidelines for handling precision. So we will use 99 places after the decimal point and trim any trailing zeros (and the decimal point, for integer case).

Also, since J does not have a "Decimal floating point number" data type, we will use a list of characters to represent a decimal or binary number (this corresponds roughly with the relevant feature set of REXX which seems to have had a strong influence on this draft of this task), and use internal (mantissa,exponent) representations during the conversion.

Implementation:

<lang J>b2b=:2 :0

 NB. string to rational number
 exp=. (1x+y i.'.')-#y
 mant=. n#.x:0"."0 y-.'.'
 number=. mant*n^exp*'.' e. y
 NB. rational number to string
 exp=. _99
 mant=. <.1r2+number*m^x:-exp
 s=. exp&(}.,'.',{.) (":m#.inv mant)-.' '
 ((exp-1)>.-+/*/\|.s e.'.0') }. s

)</lang>

Example use:

<lang J> 2 b2b 10 '23.34375' 10111.01011

  10 b2b 2 '1011.11101'

11.90625</lang>

LiveCode

LiveCode's baseConvert only works on integers. Only the first part of this task is complete. <lang LiveCode>function float2bin n

   put 15 into limit
   put 0 into i
   split n using "."
   put  baseConvert(n[1],10,2) into significand
   put "0." before n[2]
   repeat until (n[2] = 0 or i > limit)
       put n[2] * 2 into tmp
       split tmp with "."
       put tmp[1] after insignificand
       put "0." & tmp[2] into n[2]
       add 1 to i
   end repeat
   return significand & "." & insignificand

end float2bin

put float2bin(23.34375) // 10111.01011 put float2bin(11.90625) //1011.11101</lang>

Maple

<lang Maple> convert(23.34375,binary,decimal);

convert(1011.11101,decimal,binary); </lang> Output:

                          10111.01011

                          11.90625000

Mathematica

<lang Mathematica>dec2bin[x_] := Block[{digits, pos},

 {digits,  pos} = (RealDigits[ToExpression@x,  2] /. {a_, b_} :> {ToString /@ a, b});
 StringJoin @@ 
  Join@{Take[digits, pos], {"."}, Quiet@NestWhile[Most, Drop[digits, pos], Last@# === "0" &]}]

bin2dec[x_] := FromDigits[RealDigits@ToExpression@x, 2] // N

Print[NumberForm[#, {9, 5}], " => ", dec2bin@#] &@23.34375; Print[NumberForm[#, {9, 5}], " => ", NumberForm[bin2dec@#, {9, 5}]] &@1011.11101;</lang>

Output:
23.34375 => 10111.01011
1011.11101 => 11.90625

Perl 6

<lang perl6>given "23.34375" { say "$_ => ", :10($_).base(2) } given "1011.11101" { say "$_ => ", :2($_).base(10) }</lang>

Output:
23.34375 => 10111.01011
1011.11101 => 11.90625

Python

Python has float.hex() and float.fromhex() that can be used to form our own binary format. <lang python>hex2bin = dict('{:x} {:04b}'.format(x,x).split() for x in range(16)) bin2hex = dict('{:b} {:x}'.format(x,x).split() for x in range(16))

def float_dec2bin(d):

   neg = False
   if d < 0:
       d = -d
       neg = True
   hx = float(d).hex()
   p = hx.index('p')
   bn = .join(hex2bin.get(char, char) for char in hx[2:p])
   return (('-' if neg else ) + bn.strip('0') + hx[p:p+2]
           + bin(int(hx[p+2:]))[2:])

def float_bin2dec(bn):

   neg = False
   if bn[0] == '-':
       bn = bn[1:]
       neg = True
   dp = bn.index('.')
   extra0 = '0' * (4 - (dp % 4))
   bn2 = extra0 + bn
   dp = bn2.index('.')
   p = bn2.index('p')
   hx = .join(bin2hex.get(bn2[i:min(i+4, p)].lstrip('0'), bn2[i])
                for i in range(0, dp+1, 4))
   bn3 = bn2[dp+1:p]
   extra0 = '0' * (4 - (len(bn3) % 4))
   bn4 = bn3 + extra0
   hx += .join(bin2hex.get(bn4[i:i+4].lstrip('0'))
                 for i in range(0, len(bn4), 4))
   hx = (('-' if neg else ) + '0x' + hx + bn2[p:p+2]
         + str(int('0b' + bn2[p+2:], 2)))
   return float.fromhex(hx)</lang>
Output:

Run the above in idle then you can do the following interactively:

>>> x = 23.34375
>>> y = float_dec2bin(x)
>>> y
'1.011101011p+100'
>>> float_bin2dec(y)
23.34375
>>> y = float_dec2bin(-x)
>>> y
'-1.011101011p+100'
>>> float_bin2dec(y)
-23.34375
>>> float_bin2dec('1011.11101p+0')
11.90625
>>> 

Racket

The binary to number conversion is easy because it's supported by Racket. We can use string->number, wrap it in a dedicated function or use the read extension. <lang Racket>#lang racket

(define (string->number/binary x)

 (string->number x 2))

(string->number/binary "10.0101") (newline) (string->number/binary "0.01")

  1. b0.01

(string->number "0.01" 2) (newline)</lang>

Output:
2.3125

0.25
0.25
0.25

Racket only supports the number to binary conversion for integer numbers, so we multiply the original number by a power of two, to get all the binary digits, and then we manipulate the string to place the point in the correct place. <lang Racket>(define (number->string/binary x)

 (define decimals-places 10)
 (define digits-all (~r (inexact->exact (round (* x (expt 2 decimals-places))))
                        #:base 2
                        #:pad-string "0"
                        #:min-width (add1 decimals-places)))
 (define digits-length (string-length digits-all))
 (define integer-part (substring digits-all 0 (- digits-length decimals-places)))
 (define decimal-part* (string-trim (substring digits-all (- digits-length decimals-places))
                                   "0" 
                                   #:left? #f
                                   #:repeat? #t))
 (define decimal-part (if (string=? decimal-part* "") "0"  decimal-part*))
 (string-append integer-part "." decimal-part))
 

(number->string/binary 9.01) (number->string/binary 9) (number->string/binary 0.01) (newline)</lang>

Output:
"1001.000000101"
"1001.000000000"
"0.000000101"

Some additional interesting examples <lang Racket>(number->string/binary (string->number/binary "010110.0011010")) (string->number/binary (number->string/binary .1)) (newline)

(number->string/binary (string->number/binary "0.11111111111")) (string->number/binary "0.11111111111")</lang>

Output:
"10110.001101000"
0.099609375

"1.000000000"
0.99951171875

REXX

version 1

This REXX version will handle any number of digits, with bases up to 242 (using extended ASCII characters). Bases up to 62 will just use decimal digits along with upper and lowercase (Latin) letters. This REXX program is a modified version of the original program which can handle any base (no limit), and the original program did more extensive error checking. This program handles numbers with leading signs (-, +). Bases that are negative are also supported (which won't be explained here). <lang rexx>/*REXX programs converts any number in a base to another base; bases≤242*/ parse arg number toBase inBase digits . if toBase== | toBase==',' then toBase=10 /*Specified? No, use default*/ if inBase== | inBase==',' then inBase=10 /* " " " " */ if digits== | digits==',' then digits=60 /* */ if number== | number==',' then call err 'no number specified.' if \datatype(toBase,'W') then call err 'invalid toBase: ' toBase if \datatype(inBase,'W') then call err 'invalid inBase: ' inBase if \datatype(digits,'W') then call err 'invalid digits: ' digits numeric digits max(digits,length(number))+5 /*use a bigger numeric digs.*/ $=base(number,toBase,inBase) /*convert the number given. */ numeric digits digits /*use a smaller numeric digs*/ if toBase==10 then if pos('.',$)\==0 then $=format($) /*maybe use BIF*/ say number ' (in base' inBase") = " $ ' (in base' toBase")." exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────BASE subroutine─────────────────────*/ base: procedure; parse arg x 1 s 2 1 ox,tt,ii @#=0123456789; @abc='abcdefghijklmnopqrstuvwxyz'; @abcu=@abc; upper @abcu dontUse=@#'.+-'@abc || @abcu"0708090a0b0c0d"x; OK=@# || @abcu || @abc $=OK||space(translate(xrange('1'x,"fe"x),,dontUse),0) /*max base string.*/ m=length($)-1 /*"M" is the maximum base. */ if tt== then tt=10 /*assume base 10 if omitted.*/ if ii== then ii=10 /*assume base 10 if omitted.*/ i=abs(ii); t=abs(tt) if t==999 | t=="*" then t=m if t>m then call err 'invalid range for ToBase:' t"; the range is: " 2 m if i>m then call err 'invalid range for InBase:' i"; the range is: " 2 m !=substr($,1+10*(tt<0),t) /*character string for base.*/ if tt<0 then !=0 || ! /*prefix a zero if neg base.*/ if x== then return left(!,t) @=substr($, 1+10*(ii<0), i) /*@ =legal chars for base X.*/ oS= /*original sign placeholder.*/ if s='-' | s="+" then do /*process the sign (if any).*/

                      x=substr(x,2)        /*strip the sign character. */
                      oS=s                 /*save the original sign.   */
                      end

if (ii>10 & ii<37) | (ii<0 & ii>-27) then upper x /*uppercase it ? */ if pos('-',x)\==0 |, /*too many minus signs ? */

  pos('+',x)\==0 |,                        /*too many  plus signs ?    */
  x=='.'         |,                        /*is single decimal point ? */
  x==             then call err 'illegal number: ' ox

parse var x w '.' g /*sep whole from fraction. */ if pos('.',g)\==0 then call err 'illegal number: ' ox /*too many . */ items.1=0 /*# of whole part "digits". */ items.2=0 /*# of fractional "digits". */ __=w||g /*verify re-composed number.*/ _=verify(__,@'.') /*# have any unusual digits?*/ if _\==0 then call err 'illegal char in number:' ox 'char=' substr(__,_,1) if i\==10 then do /*convert # base I──►base 10*/

              _=0;  p=0                    /*convert the whole # part. */
                               do j=length(w)  to 1  by -1  while  w\==
                               _=_ + ((pos(substr(w,j,1),@)-1) * i**p)
                               p=p+1       /*increase power of the base*/
                               end   /*j*/
              w=_;  _=0;  p=1              /*convert fractional part.  */
                 do j=1 for length(g);_=_+((pos(substr(g,j,1),@)-1)/i**p)
                 p=p+1                     /*increase power of the base*/
                 end   /*j*/
              g=_
              end
         else if g\==  then g="."g       /*reinsert period if needed.*/

if t\==10 then do /*convert base10 # to base T*/

              if w\== then do            /*convert whole number part.*/
                                   do j=1;   _=t**j;   if _>w  then leave
                                   end   /*j*/
                             n=
                                 do k=j-1 to 1 by -1;   _=t**k;   d=w%_
                                 n=n || substr(!,1+d,1)
                                 w=w//_                 /*modulus = // */
                                 end     /*k*/
                             w=n||substr(!,1+w,1)
                             end
              if g\== then do;  n=       /*convert fractional part.  */
                                              do digits()+1  while g\==0
                                              p=g*t;    g=p//1;  d=p%1
                                              n=n || substr(!,d+1,1)
                                              end   /*digits()+1 ···*/
                             if n==0   then n=
                             if n\== then n='.'n   /*only a fraction?*/
                             g=n
                             end
              end

return oS || word(strip(space(w),'L',0)strip(strip(g,,0),"T",'.') 0,1) /*──────────────────────────────────ERR subroutine──────────────────────*/ err: say; say '***error!***: ' arg(1); say; exit 13</lang> output when using the input of: 23.34375 2

23.34375  (in base 10)    =    10111.01011  (in base 2).

output when using the input of: 1011.11101 10 2

1011.11101  (in base 2)    =    11.90625  (in base 10).

output when using the input of: 3.14159265358979323846264338327950288419716939937510582097494 62

3.14159265358979323846264338327950288419716939937510582097494  (in base 10)    =    3.8mHUcirZ3g3aaX5Bn156eBkfOx43HPGx7xT3yBX1Aoh3TAAEolLiHWo8Z4XVLWesfA6  (in base 62).

version 2

<lang rexx>/* REXX ---------------------------------------------------------------

  • 08.02.2014 Walter Pachl
  • --------------------------------------------------------------------*/

Call df2bf 23.34375,10111.01011 Call bf2df 1011.11101,11.90625 Call df2bf -23.34375,-10111.01011 Call bf2df -1011.11101,-11.90625 Exit

bf2df: Procedure

 Parse Arg x,soll
 If left(x,1)='-' Then Do
   sign='-'
   x=substr(x,2)
   End
 Else sign=
 Parse Var x int '.' fract
 int=reverse(int)
 vb=1
 res=0
 Do while int<>
   Parse Var int d +1 int
   res=res+d*vb
   vb=vb*2
   End
 vb=1
 Do while fract<>
   vb=vb/2
   Parse Var fract d +1 fract
   res=res+d*vb
   End
 res=sign||res
 Say sign||x '->' res
 If res<>soll Then
   Say 'soll='soll
 Return

df2bf: Procedure

 Parse Arg x,soll
 If left(x,1)='-' Then Do
   sign='-'
   x=substr(x,2)
   End
 Else sign=
 res=
 Parse Var x int '.' +0 fract
 Do While int>0
   dig=int//2
   int=int%2
   res=dig||res
   End
 If res= Then res='0'
 vb=1
 bf=
 Do i=1 To 30 while fract>0
   vb=vb/2
   If fract>=vb Then Do
     bf=bf'1'
     fract=fract-vb
     End
   Else
     bf=bf'0'
   End
 res=sign||res'.'bf
 Say sign||x '->' res
 If res<>soll Then
   Say 'soll='soll
 Return</lang>

Output:

23.34375 -> 10111.01011
1011.11101 -> 11.90625
-23.34375 -> -10111.01011
-1011.11101 -> -11.90625

Ruby

<lang ruby>def dec2bin(dec, precision=16) # String => String

 int, df = dec.split(".")
 minus = int.delete!("-")
 bin = (minus ? "-" : "") + int.to_i.to_s(2) + "."
 if df and df.to_i>0
   fp = ("."+df).to_f
   digit = 1
   until fp.zero? or digit>precision
     fp *= 2
     n = fp.to_i
     bin << n.to_s
     fp -= n
     digit += 1
   end
 else
   bin << "0"
 end
 bin

end

def bin2dec(bin) # String => String

 int, df = bin.split(".")
 minus = int.delete!("-")
 dec = (minus ? "-" : "") + int.to_i(2).to_s
 if df
   dec << (df.to_i(2) / 2.0**(df.size)).to_s[1..-1]
 else
   dec << ".0"
 end

end

data = %w[23.34375 11.90625 -23.34375 -11.90625] data.each do |dec|

 bin  = dec2bin(dec)
 dec2 = bin2dec(bin)
 puts "%10s => %12s =>%10s" % [dec, bin, dec2]

end</lang>

Output:
  23.34375 =>  10111.01011 =>  23.34375
  11.90625 =>   1011.11101 =>  11.90625
 -23.34375 => -10111.01011 => -23.34375
 -11.90625 =>  -1011.11101 => -11.90625

Tcl

By far the easiest way to do this is to use Tcl's built-in handling of IEEE arithmetic, converting the IEEE representation into the string representation we want (and vice versa) by simple string manipulations.

Works with: Tcl version 8.6

<lang tcl>package require Tcl 8.6

proc dec2bin x {

   binary scan [binary format R $x] B* x
   regexp {(.)(.{8})(.{23})} $x -> s e m
   binary scan [binary format B* $e] cu e
   if {$e == 0 && ![string match *1* $m]} {

# Special case for zero set m 0.0

   } else {

incr e -127

set m 1$m if {$e < 0} { set m [string repeat "0" [expr {-$e}]]$m set m [string trimright [regsub {^.} $m "&."] "0"] } else { set m [string trimright [regsub ^.[string repeat . $e] $m "&."] "0"] } if {[string match *. $m]} { append m 0 }

   }
   if {$s} {

return -$m

   } else {

return $m

   }

} proc bin2dec x {

   if {[regexp {^-} $x]} {

set s 1 set x [string trimleft $x -0]

   } else {

set s 0 set x [string trimleft $x +0]

   }
   lassign [split [string trimright $x 0] .] fore aft
   if {[string length $fore]} {

set e [expr {[string length $fore] - 1}] set digits [string range $fore$aft 1 end]

   } elseif {[string length $aft]} {

set digits [string range [string trimleft $aft 0] 1 end] set e [expr {[string length $digits] - [string length $aft]}]

   } else {

set e -127 set digits {}

   }
   incr e 127
   binary scan [binary format B* [format %b%08b%-023s $s $e $digits]] R x
   return $x

}

foreach case {77 0.25 0.15625 0.1 -33.8 0 1 2 3 23.34375 11.90625} {

   set b [dec2bin $case]
   set d [bin2dec $b]
   puts "$case => $b => $d"

}</lang>

Output:
77 => 1001101.0 => 77.0
0.25 => 0.01 => 0.25
0.15625 => 0.00101 => 0.15625
0.1 => 0.000110011001100110011001101 => 0.10000000149011612
-33.8 => -100001.110011001100110011 => -33.79999923706055
0 => 0.0 => 0.0
1 => 1.0 => 1.0
2 => 10.0 => 2.0
3 => 11.0 => 3.0
23.34375 => 10111.01011 => 23.34375
11.90625 => 1011.11101 => 11.90625

Adapting the code to work with IEEE double-precision floats is left as an exercise for the reader, as is dealing with the trickier special cases of the infinities and NaN.

zkl

Rather limited. <lang zkl>fcn bin2float(bstr){ // no exponents

  bstr=bstr.strip();
  m:=bstr[0,1]=="-"; if(m)bstr=bstr[1,*]; m=m and -1 or 1;
  a,b:=bstr.split(".").apply(fcn(s){ s and s or 0 }).append(0,0);
  (a.toInt(2).toFloat() + b.toInt(2).toFloat()/(2).pow(b.len()))*m

}</lang> <lang zkl>foreach bstr in (T("1011.11101","0.01","0.11111111111","-.1","","1")){

  println(bstr," --> ",bin2float(bstr).toString(20))

}</lang>

Output:
1011.11101 --> 11.90625
0.01 --> 0.25
0.11111111111 --> 0.99951171875
-.1 --> -0.5
 --> 0
1 --> 1

<lang zkl>fcn float2bin(x,digitsOfPrecision=20){

  m,zeros:="","0"*digitsOfPrecision;
  if(x<0){ m="-"; x=-x }
  a,b:=x.modf();  // int and fractional parts
  b=(b*(2).pow(digitsOfPrecision)).round().toInt().toString(2);
  b=zeros[0,digitsOfPrecision-b.len()] + b;   // don't drop leading zeros
  if(z:=b.reverse().prefix(zeros)) b=b[0,-z]; // remove trailing zeros
  String(m,a.toString(2),".",b);   

}</lang> <lang zkl>foreach x in (T(23.34375,(0.0).pi,-33.8,0.1,0.15625)){

  println(x," --> ",s:=float2bin(x)," --> ",bin2float(s).toString(20));

}</lang>

Output:
23.3438 --> 10111.01011 --> 23.34375
3.14159 --> 11.00100100001111110111 --> 3.1415929794311523438
-33.8 --> -100001.11001100110011001101 --> -33.800000190734863281
0.1 --> 0.0001100110011001101 --> 0.1000003814697265625
0.15625 --> 0.00101 --> 0.15625