Decimal floating point number to binary: Difference between revisions
(+ D entry) |
(added haskell float to binary part) |
||
Line 75: | Line 75: | ||
-23.3438 |
-23.3438 |
||
11.906250</pre> |
11.906250</pre> |
||
=={{header|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> |
|||
{{out}} |
|||
<pre> |
|||
0.1011101011p+101 |
|||
</pre> |
|||
=={{header|J}}== |
=={{header|J}}== |
Revision as of 08:20, 24 November 2014
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
<lang d>import std.stdio, std.conv, std.array, std.string, std.range, std.algorithm, std.typecons;
immutable string[string] hex2bin, bin2hex;
pure @safe static this() {
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*/ {
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 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() {
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
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#.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>
Maple
<lang Maple> convert(23.34375,binary,decimal);
convert(1011.11101,decimal,binary); </lang> Output:
10111.01011 11.90625000
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 >>>
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.
<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.