converting a 17-bit unsigned binary number to double-type

I have a question on converting a 17-bit unsigned binary number to double-type. Let say the binary number is 1_0011_0110_1110_1001; // which is 0.60725 in decimal

How would I approach this ?

Note:

dec2bin 0.60725 gives 0.10011011011101001
dec2bin 0.60725 gives 0.1_0011_0110_1110_1001
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <bitset>
#include <string>
#include <limits>
#include <iomanip>

template < std::size_t N = 17 > // input: bits of the N-bit significand
double bits_to_double( const std::string& bits )
{
    static_assert( N < std::numeric_limits<unsigned long long>::digits, "overflow" ) ;

    static constexpr auto denom = 1ULL << N ;

    std::string mantissa ; // remove non-digit characters
    for( char c : bits ) if( c == '1' || c == '0' ) mantissa += c ;
    mantissa.resize( N, '0' ) ; // truncate / append zeroes to make N bits

    return double( std::bitset<N>(mantissa).to_ullong() ) / denom ;

}

int main()
{
    std::cout << std::fixed << std::setprecision(5)
              << bits_to_double<17>( "1_0011_0110_1110_1001" ) << '\n' ;
}

http://coliru.stacked-crooked.com/a/048a8fafd1a2c868
@JLBorges :

return double( std::bitset<N>(mantissa).to_ullong() ) / denom ;

Why do we need to divide by 'denom' ? What is the exact reason ?
There is an implied binary point before the most significant binary digit of the significand.

For example: 0.1011 == 1011 / 10000 (binary)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

//======================================================================

double baseDec( string s, int base )
{
   // Keep digits and point
   string digits;
   string fraction;
   for ( char c : s ) if ( c == '.' || isdigit( c ) ) digits += c;    // currently allows up to base 10

   // Split into whole and fractional part
   int pos = digits.find( '.' );
   if ( pos != string::npos )          
   {
      fraction = digits.substr( pos + 1 );
      digits = digits.substr( 0, pos );
   }

   double result = 0;

   // Compound whole-number part
   for ( int i = 0; i < digits.size(); i++ ) result = base * result + ( digits[i] - '0' );

   // Compound fractional part
   double value = 1.0 / base;
   for ( int i = 0; i < fraction.size(); i++ )
   {
       if ( fraction[i] != 0 ) result += value * ( fraction[i] - '0' );
       value /= base;
   }

   return result;
}

//======================================================================

int main()
{
   string s;
   int base;

   cout << "Input base (2-10): ";
   cin >> base;
   cout << "Input unsigned number in that base (with/without point): ";
   cin >> s;

   cout << "Decimal is " << fixed << setprecision( 5 ) << baseDec( s, base ) << endl;
}


Input base (2-10): 2
Input unsigned number in that base (with/without point): 0.10011011011101001
Decimal is 0.60725


Input base (2-10): 7
Input unsigned number in that base (with/without point): 24.11
Decimal is 18.16327
@JLBorges @lastchance

Thanks for the function on unsigned binary number strings.
I wish to change it to suit input for SIGNED binary number strings.

I am having problem modifying your template function. Any advice ?
http://cpp.sh/2kapf

I am getting verbose output:
bits = 1100
bits = 0100
dxn = 6.50521e-19
dyn = 0.5

dxn should be -0.5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <bitset>
#include <string>
#include <limits>
#include <iomanip>

double dxn, dyn;

// Reference: http://www.cplusplus.com/forum/general/216385/
template < std::size_t N = 4 > // input: bits of the N-bit significand
double bits_to_double( const std::string& bits )
{
    std::cout << "bits = " << bits << '\n'; 
    static_assert( N < std::numeric_limits<signed long long>::digits, "overflow" ) ;

    static constexpr auto denom = 1ULL << (N-1) ;

    std::string mantissa ; // remove non-digit characters
    for( char c : bits ) 
	if( c == '1' || c == '0' ) 
	{
	   mantissa += c ; 
	   //std::cout << "Loop  , mantissa = " << mantissa << '\n'; 
	}
    
    //std::cout << "before, mantissa = " << mantissa << '\n';
    mantissa.resize( N, '0' ) ; // truncate / append zeroes to make N bits
    //std::cout << "after , mantissa = " << mantissa << '\n';
    
    if (mantissa[0] == '0')  // positive number
    	return double( std::bitset<N>(mantissa).to_ullong() ) / denom ;
    else
	return double( std::bitset<N>(mantissa).to_ullong() ) / (-denom) ;
}

int main()
{
  
  dxn = bits_to_double<4>( std::bitset<4>(8).to_string() );
  dyn = bits_to_double<4>( std::bitset<4>(4).to_string() ); 
  
  std::cout << "dxn = " << dxn << '\n';
  std::cout << "dyn = " << dyn << '\n';
}
Last edited on
bits = 1100
bits = 0100
dxn = 6.50521e-19
dyn = 0.5

dxn should be -0.5


dxn = bits_to_double<4>( std::bitset<4>(8).to_string() );
8 is 1000, not 1100, in binary.

Could you explain what/how you wish your bits to indicate the sign - there are various possible representations.
Last edited on
Is this what you intended? (It's a fairly rare way of using sign bits - see https://en.wikipedia.org/wiki/Signed_number_representations#Signed_magnitude_representation for the alternatives).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <bitset>
#include <string>
#include <limits>
#include <iomanip>

double dxn, dyn;

// Reference: http://www.cplusplus.com/forum/general/216385/
template < std::size_t N = 4 > // input: bits of the N-bit significand
double bits_to_double( const std::string& bits )
{
    std::cout << "bits = " << bits << '\n'; 
    static_assert( N < std::numeric_limits<signed long long>::digits, "overflow" ) ;

    static constexpr auto denom = 1ULL << (N-1) ;

    std::string mantissa ; // remove non-digit characters
    for( char c : bits ) 
        if( c == '1' || c == '0' ) 
        {
           mantissa += c ; 
           //std::cout << "Loop  , mantissa = " << mantissa << '\n'; 
        }
    
    //std::cout << "before, mantissa = " << mantissa << '\n';
    mantissa.resize( N, '0' ) ; // truncate / append zeroes to make N bits
    //std::cout << "after , mantissa = " << mantissa << '\n';
    
    int sign = ( mantissa[0] == '0' ? 1 : -1 );                             // <=====
    mantissa[0] = '0';                                                      // <=====
    return sign * double( std::bitset<N>(mantissa).to_ullong() ) / denom ;  // <=====
}

int main()
{
  
  dxn = bits_to_double<4>( std::bitset<4>(12).to_string() );   // <==== NOTE
  dyn = bits_to_double<4>( std::bitset<4>(4).to_string() ); 
  
  std::cout << "dxn = " << dxn << '\n';
  std::cout << "dyn = " << dyn << '\n';
}


bits = 1100
bits = 0100
dxn = -0.5
dyn = 0.5
Last edited on
@lastchance :

Thanks.

I am trying to convert two-complement binary string to signed decimal.

http://cpp.sh/7vo3y

I am getting -0.9375 instead of -1
What is wrong with the code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Example program
#include <iostream>
#include <string>
#include <bitset>

int main()
{
    static auto denom = 1ULL << 4 ;                  //  implied binary decimal point 
    std::string mantissa = "111111110000";       //  -1.0000  in two-complement form
    
    int sign = ( mantissa[0] == '0' ? 1 : -1 );                            
    //std::cout << "sign = " << sign << endl;

    if (sign == -1){		// two-complement 		
		int temp = std::stoi(mantissa, nullptr, 2);
		temp = temp -1;

		mantissa = std::to_string(temp);
        //std::cout << "mantissa = " << mantissa << '\n';
		int j=0;
		
        for( char c : mantissa ){
		    if(c  == '1') 
		       mantissa[j] = '0' ; 
		    else
		       mantissa[j] = '1' ;

		    j++;
	    }
	    //std::cout << "mantissa = " << mantissa << '\n';
    }
    //std::cout << "mantissa = " << mantissa << '\n';
    
    double answer = sign * double( std::bitset<12>(mantissa).to_ullong() ) / denom ; 
    
    std::cout << "answer = " << answer << '\n';
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
double bits_to_double( std::string bits, std::size_t bits_after_pt ) {

    // sanity checks
    if( bits.size() < bits_after_pt ) return NAN ;
    if( bits.size() == bits_after_pt ) bits = '0' + bits ;

    const auto bits_before_pt = bits.size() - bits_after_pt ;
    auto whole_part = bits.substr( 0, bits_before_pt ) ;
    const auto frac_part = bits.substr(bits_before_pt) ;

    const int sign = whole_part[0] == '0' ? 1 : -1 ;
    if( sign == -1 ) {

        whole_part = std::to_string( std::stoi(whole_part) - 1 ) ;
        for( char& c : whole_part ) c = ( c == '1' ? '0' : '1' ) ;
    }

    const double denom = 1ULL << bits_after_pt ;
    const double frac_part_value = std::bitset<64>(frac_part).to_ullong() / denom ;

    return sign * ( std::bitset<64>(whole_part).to_ullong() + frac_part_value ) ;
}

http://coliru.stacked-crooked.com/a/9ebe0c341cd35e70
@JLBorges :

Seems like using std::stoi(whole_part, nullptr, 2) will give us 254 instead of 11111111

http://coliru.stacked-crooked.com/a/d88fdd0b090e30b6
May I know why ?

This is the key to understanding why I am getting -0.9375 instead of -1 in the previous post.

If I do not use std::stoi(whole_part, nullptr, 2), I will encountered error regarding INT_MAX
Last edited on
The last argument to std::stoi is the radix (the base of the number system).

std::stoi( "101", nullptr, 2 ) == 5
std::stoi( "101", nullptr, 8 ) == 65
std::stoi( "101", nullptr, 10 ) == 101
std::stoi( "101", nullptr, 16 ) == 257



> If I do not use std::stoi(whole_part, nullptr, 2), I will encountered error regarding INT_MAX

Use std::stoll(whole_part)
http://coliru.stacked-crooked.com/a/76b2516e4bbd80e0
Topic archived. No new replies allowed.