thanos1983 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I have managed to convert positive and negative decimal numbers to binary and vice versa.

Final Update (floating points (+/-) to binary and vice versa for 32 bit and 64 bit accuracy), the code is at the end in case of future reference.

Sample of code:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; sub dec2bin { my $bits = shift; my $str = unpack("B8", pack("c", $bits)); return $str; } sub bin2dec { return unpack("c", pack("B8", substr("0" x 8 . shift , -8))); } my @array = (127,-127); foreach $_ (@array){ if ($_ < 0) { print "Value: $_ is negative\n"; my $negative_b = dec2bin($_); print "Negative binary: ".$negative_b."\n"; my $negative_d = bin2dec($negative_b); print "Negative_binary back to decimal: ".$negative_d."\n"; } else { print "Value: $_ is possitive\n"; my $possitive_b = dec2bin($_); print "Possitive binary: ".$possitive_b."\n"; my $possitive_d = bin2dec($possitive_b); print "Possitive_binary back to decimal: ".$possitive_d."\n"; } } __END__ Value: 127 is possitive Possitive binary: 01111111 Possitive_binary back to decimal: 127 Value: -127 is negative Negative binary: 10000001 Negative_binary back to decimal: -127

I am trying to figure out how to convert positive and negative floating points. For example if I had a number -0.000008 how could I convert the number in binary and back to floating.

I have read on the pack documentation that: F A Perl internal floating-point value (NV) in native format

When I am modifying on my code the "c" to "F" I get the following error:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; sub dec2bin { my $bits = shift; my $str = unpack("B8", pack("F", $bits)); return $str; } sub bin2dec { return unpack("F", pack("B8", substr("0" x 8 . shift , -8))); } my @array = (0.000008,-0.000008); foreach $_ (@array){ if ($_ < 0) { print "Value: $_ is negative\n"; my $negative_b = dec2bin($_); print "Negative binary: ".$negative_b."\n"; my $negative_d = bin2dec($negative_b); print "Negative_binary back to decimal: ".$negative_d."\n"; } else { print "Value: $_ is possitive\n"; my $possitive_b = dec2bin($_); print "Possitive binary: ".$possitive_b."\n"; my $possitive_d = bin2dec($possitive_b); print "Possitive_binary back to decimal: ".$possitive_d."\n"; } } __END__ Value: 8e-06 is possitive Possitive binary: 10001101 Use of uninitialized value $possitive_d in concatenation (.) or string + at negative2bin&bin2negative.pl line 31. Possitive_binary back to decimal: Value: -8e-06 is negative Negative binary: 10001101 Use of uninitialized value $negative_d in concatenation (.) or string +at negative2bin&bin2negative.pl line 24. Negative_binary back to decimal:

So I assume I am going terribly wrong some where. What I am missing?

Final Update:

Thanks to everyone's replies I manage to get this fixed, and understand that floating point 8 bits value is extremely small and not even worth existing.

In IEEE 754-2008 the 16-bit base 2 format is officially referred to as binary16. It is intended for storage (of many floating-point values where higher precision need not be stored), not for performing arithmetic computations. Half-precision floating-point format

So in conclusion I decided to use 32 bit floating points for positive and negative values.

Sample of code provided bellow:

#!/usr/bin/perl use strict; use warnings; my @array = (3.987654321,-3.987654321); foreach $_ (@array){ if ($_ < 0) { print "Value: $_ is negative\n"; my $negative_b = dec2bin($_,32,"f"); print "Negative binary: ".$negative_b."\n"; my $negative_d = bin2dec($negative_b,32,"f"); print "Negative_binary back to decimal: ".$negative_d."\n"; } else { print "Value: $_ is possitive\n"; my $possitive_b = dec2bin($_,32,"f"); print "Possitive binary: ".$possitive_b."\n"; my $possitive_d = bin2dec($possitive_b,32,"f"); print "Possitive_binary back to decimal: ".$possitive_d."\n"; } } sub dec2bin { my $bits = shift; my $size = shift; my $template = shift; my $str = unpack("B$size", pack($template, $bits)); print "Substring: ".$str."\n"; return $str; } sub bin2dec { my $bits = shift; my $size = shift; my $template = shift; return unpack($template, pack("B$size", substr("0" x $size . $bits + , -$size))); } __END__ Value: 3.987654321 is possitive Substring: 10111010001101010111111101000000 Possitive binary: 10111010001101010111111101000000 Possitive_binary back to decimal: 3.98765420913696 Value: -3.987654321 is negative Substring: 10111010001101010111111111000000 Negative binary: 10111010001101010111111111000000 Negative_binary back to decimal: -3.98765420913696

In case the user wants to round up the values he can use functions like sprintf() or printf() etc.

For example: printf("%.8f", $possitive_d); # prints 3.98765421 or my $rounded = sprintf("%.8f", $negative_d); # prints: -3.98765421 etc.

For floating points 64 bits change the template from f to F and size from 32 to 64 an.

Sample of working code provided under:

#!/usr/bin/perl use strict; use warnings; my @array = (3.987654321,-3.987654321); foreach $_ (@array){ if ($_ < 0) { print "Value: $_ is negative\n"; my $negative_b = dec2bin($_,64,"F"); print "Negative binary: ".$negative_b."\n"; my $negative_d = bin2dec($negative_b,64,"F"); print "Negative_binary back to decimal: ".$negative_d."\n"; } else { print "Value: $_ is possitive\n"; my $possitive_b = dec2bin($_,64,"F"); print "Possitive binary: ".$possitive_b."\n"; my $possitive_d = bin2dec($possitive_b,64,"F"); print "Possitive_binary back to decimal: ".$possitive_d."\n"; } } sub dec2bin { my $bits = shift; my $size = shift; my $template = shift; my $str = unpack("B$size", pack($template, $bits)); print "Substring: ".$str."\n"; return $str; } sub bin2dec { my $bits = shift; my $size = shift; my $template = shift; return unpack($template, pack("B$size", substr("0" x $size . $bits + , -$size))); } __END__ Value: 3.987654321 is possitive Substring: 10101110100101010000001101001111101101111110011000001111010 +00000 Possitive binary: 1010111010010101000000110100111110110111111001100000 +111101000000 Possitive_binary back to decimal: 3.987654321 Value: -3.987654321 is negative Substring: 10101110100101010000001101001111101101111110011000001111110 +00000 Negative binary: 10101110100101010000001101001111101101111110011000001 +11111000000 Negative_binary back to decimal: -3.987654321

Thanks again for everyone's assistance to help me understand the process and provided me so much material to read on.

Seeking for Perl wisdom...on the process of learning...not there...yet!

Replies are listed 'Best First'.
Re: How to convert negative and positive floating points to binary and vice versa
by AnomalousMonk (Archbishop) on Sep 20, 2014 at 05:19 UTC

    I'm far from an expert on internals, but IIRC, the "internal representation" of a number in Perl is as a binary 64-bit IEEE-754 float. You've only been looking at 8 bits (B8) of the 64. Note that the positive and negative numbers below differ in one single bit in their binary FP representations.

    c:\@Work\Perl\monks>perl -wMstrict -le "my @ra = (0.000008, -0.000008); ;; for my $fpn (@ra) { my $pfpn = pack 'F', $fpn; printf qq{%8s: length when F-packed: %d bytes \n}, $fpn, length $pf +pn; } ;; for my $fpn (@ra) { my $pfpn = pack 'F', $fpn; printf qq{%8s: hex '%s' \n}, $fpn, unpack 'H*', $pfpn; } ;; for my $fpn (@ra) { my $pfpn = pack 'F', $fpn; printf qq{%8s: bits '%s' \n}, $fpn, unpack 'B*', $pfpn; } " 8e-006: length when F-packed: 8 bytes -8e-006: length when F-packed: 8 bytes 8e-006: hex '8dedb5a0f7c6e03e' -8e-006: hex '8dedb5a0f7c6e0be' 8e-006: bits '100011011110110110110101101000001111011111000110111000 +0000111110' -8e-006: bits '100011011110110110110101101000001111011111000110111000 +0010111110'

    Update: See Wikipedia article IEEE floating point. (Update: Oh, and also see What Every Computer Scientist Should Know About Floating-Point Arithmetic and What Every Computer Scientist Should Also Know About Floating-Point Arithmetic — how could I forget?)

    Update 2: Changed code example to include hex unpacking.

      Hello AnomalousMonk,

      Thank you for your time and effort, it start to makes sense what you said. The only problem is that I would like to have an 8 bit negative/positive floating point.

      But I will try to make it work for 64 bit and maybe work my way up from there.

      Thanks again.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
        I would like to have an 8 bit negative/positive floating point.

        You want an 8-bit floating point value. Why?

        A floating point value consists of 3 parts:

        1. 1-bit to indicate the sign.
        2. A number of the remaining bits denote the scale of the value. (The exponent, characteristic or scale.)

          This scales the size of the value, and 1-bit indicates whether it is either bigger or smaller.

        3. A number of bits that represent the actual value. (The significand, coefficient or the mantissa.)

          These bits define the precision of the values tha can be represented.

        Assuming that you go for 4 bits of precision, that leaves 3 bits for scaling, means you could represent numbers between +/- 0.001 and +/- 150, which is a pretty useless format.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        An eight-bit floating point representation would seem to offer an extremely limited dynamic range, but whatever floats your boat... According to the WP article referenced above, IEEE-754 already has a "binary16" Half-precision floating-point format definition; maybe that would be of use.

        So you want a floating-point number packed into 8 bits? Why?

        I'm not sure if there is a standardized format for those, since they're not all too useful, I've only found them referenced in tutorials and university courses on floating-point numbers for students to practice on (e.g. http://sandbox.mc.edu/~bennet/cs110/textbook/module4_2.html or http://www.toves.org/books/float/#s2 ). Wikipedia has an article on Minifloats. Perl doesn't have a built-in conversion for that.

        The smallest standardized IEEE format is 16 bits, and even those have their limitations.

Re: How to convert negative and positive floating points to binary and vice versa
by Laurent_R (Canon) on Sep 20, 2014 at 14:13 UTC
    I wonder why you want to do that. This almost always unneeded in Perl, because Perl usually does the conversion for you. Internally, numbers are used as binary numbers and Perl usually computes with double-precision floating-point values. When you enter a number in decimal format, Perl usually stores it in binary format (well, in some cases, it might initially store it as a string if Perl does not know that it is supposed to be a number, but it will do the conversion when you use that string as a number, for example in an arithmetic operation). When you are trying to "convert" numbers either way, you actually don't convert them, you are only converting string representations of these numbers. When you write:
    my $value1 = 255; my $value2 = 0377; # 377 octal, equal to 255 decimal my $value3 = 0xff; # FF hex, same as 255 decimal my $value4 = 0b11111111; # also 255 decimal
    all these values are the same to Perl.

    The only thing for which I can see the conversions you are trying to do to be useful is for input and output of special representations of those numbers. Or, of course, for training purposes but you will not have to use that very often.

    I can remember of one single case where I had to do that in Perl, for modeling a game with white and black stones where is was convenient to represent the game board (and the moves) with 16-bit integers, where, for example, a 0 would represent a white stone and 1 a black stone (still, although my program was using heavily the bit-wise operators, I needed binary representation only for the purpose of displaying conveniently debugging information, the program itself did not need it).

    The octal representation is sometimes needed for such thing as Unix file permissions. Sometimes you might use bit vectors and need to see what it looks like, or need to visualize individual bits for other low level operations, but it is still rather uncommon in day to day Perl programming to need to use the binary representation of numbers.

      Hello Laurent_R,

      Wow really nice explanation, it helped my to understand many things. Well I was planning to increase the accuracy of a timing process that I want to use, but as everyone pointed out the number that I am going to get is so small that probably will be out of range.

      At this point I think I should look for 32 bit floating points with positive and negative range values. I think they will do my work just fine, and in case that they can I will take it from there and start digging again to my next problem.

      Again I want to say thank you, for your time and effort. You helped me to understand a lot.

      Seeking for Perl wisdom...on the process of learning...not there...yet!