BigInteger:在 C++ 中使用 ofstream 写入文件时将基数更改为 2 的方法?

BigInteger: Way to change base to 2 when writing to file using ofstream in C++?

我正在使用 C++ 中的 BigInteger 库来乘以一些大数并将它们写入文件。但是,我想在编写数字之前更改数字的基数。

我试过使用 std::setbase() 但它只会将其更改为基数 8、10 和 16。例如尝试 setbase(2) 会出错。

这是一个例子:

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include "BigIntegerLibrary.hh"
#include "BigIntegerUtils.hh"
#include "BigIntegerAlgorithms.hh"
#include "BigUnsigned.hh"
#include "BigUnsignedInABase.hh"
#include "BigInteger.hh"
#include <stdio.h>      /* printf, NULL */
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */
...
        str1=randomStrGen(k);
        str2=randomStrGen(j);

        BigInteger s1 = stringToBigInteger(str1);
        BigInteger s2 = stringToBigInteger(str2);

        myfile<<str1<<" "<<str2<<" "<<"10"<<" "<<setbase(2)<<s1+s2<<" "<<s1*s2<<"\n";
...

给出错误:

terminate called after throwing an instance of 'char const*'

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
[Inferior 1 (process 5156) exited with code 03]

BigInteger 库下载自 https://mattmccutchen.net/bigint/,2010.04.30 发布。

问题是 std::setbase(2) 没有 切换到二进制表示,请参阅 here on cppreference:

Values of base other than 8, 10, or 16 reset basefield to zero, which corresponds to decimal output and prefix-dependent input.

这是一个自己的实现,用于获取将存储到 std::string 中的 BigUnsigned 的二进制表示,请注意,它假定您的机器将值存储在 little 中endian 表示。

函数 convertToBinary() 需要一个 BigUnsigned 因为这种类型可以执行更多的数学运算,例如获取位。这也意味着它只能用于 无符号数 .

此外,您应该考虑使用其他库,因为该库将不再维护(2010 年的最后一个版本),如 here:

所述

I have lost interest in maintaining this library and will not respond to most development or support inquiries.

完整代码:

#include "BigIntegerLibrary.hh"    
#include <iostream>
#include <string>

std::string convertToBinary(const BigUnsigned& bigUns)
{
   std::string binary;
   for (size_t idx = 0; idx < bigUns.bitLength(); ++idx)
   {
      binary += (bigUns.getBit(bigUns.bitLength() - 1 - idx) ? "1" : "0");
   }

   return binary;
}

int main()
{
   std::string str1 = "12";
   std::string str2 = "18446744073709551615"; /* uint64 max */
   BigInteger s1 = stringToBigInteger(str1);
   BigInteger s2 = stringToBigInteger(str2);
   s2 = s2 * 2;

   std::cout << s1 << ": " << convertToBinary(s1.getMagnitude()) << std::endl;
   std::cout << s2 << ": " << convertToBinary(s2.getMagnitude()) << std::endl;

   return 0;
}

输出:

12: 1100
36893488147419103230: 11111111111111111111111111111111111111111111111111111111111111110

对于本机类型,您可以获得二进制表示,如评论中所述并已回答


编辑#1:

由于 OP 在下面的评论中谈到了与任何基数的一般转换,这里是一个将 BigUnsigned 转换为返回 std::string 的任何基数的版本,函数名称是 convertToAnyBase()。它的灵感来自 this snippet。但这只适用于无符号数,就像之前的解决方案一样。
要检查那些大数字转换,有一个很棒的在线工具,叫做 numberfacts

完整代码(也包含上面的代码):

#include "BigIntegerLibrary.hh"    
#include <iostream>
#include <string>
#include <vector>

std::string convertToBinary(const BigUnsigned& bigUns)
{
   std::string binary;
   for (size_t idx = 0; idx < bigUns.bitLength(); ++idx)
   {
      binary += (bigUns.getBit(bigUns.bitLength() - 1 - idx) ? "1" : "0");
   }

   return binary;
}

std::string convertToAnyBase(const BigUnsigned& bigUns_P, unsigned short base)
{
   static const char baseDigits[16] =
      { '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

   BigUnsigned bigUns = bigUns_P;

   std::string anyBaseString;
   std::vector<size_t> convertedNumber;
   convertedNumber.reserve(bigUns.bitLength());

   /* convert to the indicated base */
   while (bigUns != 0)
   {
      convertedNumber.push_back((bigUns % base).toUnsignedInt());
      bigUns = bigUns / base;
   }

   /* store result in reverse order */
   for (std::vector<size_t>::const_reverse_iterator curNumber = convertedNumber.rbegin();
        curNumber != convertedNumber.rend();
        ++curNumber)
   {
      anyBaseString += baseDigits[*curNumber];
   }

   return anyBaseString;
}

int main()
{
   std::string str1 = "12";
   std::string str2 = "18446744073709551615"; /* uint64 max */
   BigInteger s1 = stringToBigInteger(str1);
   BigInteger s2 = stringToBigInteger(str2);
   s2 = s2 * 2;

   std::cout << s1 << " binary: " << convertToBinary(s1.getMagnitude()) << "\n"
             << s2 << " binary: " << convertToBinary(s2.getMagnitude()) << "\n"
             << std::endl;

   std::cout << s1 << " binary: " << convertToAnyBase(s1.getMagnitude(), 2) << "\n"
             << s2 << " binary: " << convertToAnyBase(s2.getMagnitude(), 2) << "\n"
             << std::endl;

   std::cout << s1 << " octal: "  << convertToAnyBase(s1.getMagnitude(), 8) << "\n"
             << s2 << " binary: " << convertToAnyBase(s2.getMagnitude(), 8) << "\n"
             << std::endl;

   return 0;
}

输出:

12 binary: 1100
36893488147419103230 binary: 11111111111111111111111111111111111111111111111111111111111111110

12 binary (any base function): 1100
36893488147419103230 binary (any base function): 11111111111111111111111111111111111111111111111111111111111111110

12 octal (any base function): 14
36893488147419103230 octal (any base function): 3777777777777777777776

编辑#2:

转换为任何基数的更简单方法是使用 BigInteger 库提供的功能。它提供了 BigUnsignedInABase class 支持转换为 std::string,但这也只适用于 unsigned numbers:

std::string convertToAnyBase(const BigUnsigned& bigUns, unsigned short base)
{
   BigUnsignedInABase bigBase(bigUns, base);
   return static_cast<std::string> (bigBase);
}

如果您还想使用带符号的数字,则必须使用可用的 BigInteger 库中的 Two's complement. There is no operator ! (Two's complement) or operator ~ (One's complement),因此您必须在位上执行此操作并为您自己添加一个。

但这只适用于二进制(基数 2)转换。其他基础的问题是 BigInteger 库将 BigInteger 保存为符号 + BigUnsigned 作为聚合数,因此这里没有负数的任何其他二进制表示。

另一种可能是在字符串前面写上符号(BigInteger::getSign())并保留正数例如:

-12base{10} = -1100base{2} = -14base{8} 

这是一个简单的代码更改,因为您只需传递 BigInteger 而不是 BigUnsigned 并使用适当的方法。