【发布时间】:2018-11-08 08:58:24
【问题描述】:
我目前正在研究一个概念,并且在我提出这个问题时正在编写伪代码。我正在考虑制作一个相当简单易用的类接口来表示 BigInts。我正在考虑制作几个具有 BigInt 类将使用的基本属性和成员的简单结构。例如,它不是直接处理负值的 BigInt 类,而是包含一个 Sign 结构,并且这个结构基本上包含一个值 0 或 1,或者基本上是一个布尔类型来指定这个 BigInt 是正数还是负数。在构建时,我打算让该类默认生成一个正数。我还想有一个结构来表示有两个变体的数字。第一个变体具有数字 0-9,第二个将继承原始变体,但也包括 A-F。这样,作为模板类但只有两种有效类型的类将支持十进制和十六进制的使用。所有数学运算符都将在类之外定义,并且根据其推断类型,它将调用并执行适当版本的函数。然而,十六进制部分仍然只是概念,因为我想首先启动并运行十进制版本。这些帮助类可能看起来像这样:
class Sign {
private:
bool isNegative_ { false };
char sign_;
public:
Sign() : isNegative_( false ) {
sign_ = '+';
}
Sign( const bool isNegative ) : isNegative_( isNegative ) {
sign_ = isNegative_ ? '-' : '+';
};
Sign( const char sign ) {
if ( sign == '+' || sign == '\0' || sign == ' ' ) {
isNegative_ = false;
} else if ( sign == '-' ) {
isNegative_ = true;
} else {
// throw error improper character.
}
}
bool isPostive() const { return !isNegative_; }
bool isNegative() const { return !isNegative; }
char sign() const { return sign_; }
char negate() {
isNegative_ = !isNegative_;
sign_ = isNegative_ ? '+' : '-';
return sign_;
}
};
// NST = NumberSystemType
class enum NST { Decimal = 10, Hexadecimal = 16 };
template<class NT> // NT = NumberType
class Digit {
private:
NST nst_; // determines if Decimal or Hexadecimal
};
// Specialization for Decimal
template<NST::Decimal> // Should be same as template<10>
class Digit {
// would like some way to define 4 bits to represent 0-9; prefer not to
// use bitfields, but I think a char or unsigned char would be wasting
// memory using twice as much for what is needed. Not sure what to do here...
// maybe std::bitset<>...
};
template<NST::Hexadecimal> // Should be same as template<16>
class Digit : public Digit<Decimal> { // Also inherits all of The first specialization.
// same as above only needs 4 bits to represent values form 0-F
// A half char would be excellent but does not exist in c++... and most
// programming language's types.
// still thinking of maybe std::bitset<>...
};
两者之间的主要区别在于,第一个特化只允许 0-9 的数字值和 0-9 的数字本身,而第二个没有这个限制,但也允许从 a-f 和或 A-F 任一种情况已验证。我还可以包含一个 const char* 来指定 0x 的十六进制前缀,它将附加到任何包含的值以进行显示。
我喜欢这种设计方法,因为我希望将 BigInt 类的实际算术函数和运算符保留为单独的函数模板,因为 BigInt 类可以支持 Decimal 和 Hexadecimal 专用模板类型。如果一切顺利,我还想添加支持以处理复数。
BigInt 类应该是这样的:
template<class NT>
BigInt {
private:
Sign sign_;
Digit<NT> carryDigit_;
std::vector<Digit<NT>> value_;
// May contain some functions such as setters and getters only
// Do not want the class to be modifying itself except for assignment only.
};
如上所述,这也适用于十进制和十六进制类型,但是如果有人创建了 BigInt myBigInt 的实例,则默认为十进制!
对于包含在向量中的数据。我想以与阅读内容相反的顺序存储数字。因此,如果它们是 BigInt 内部向量中的数字345698,它将被存储为896543。这样做的原因是,当我们在数学中进行算术运算时,我们从小数点左侧的右侧开始从最低有效位到最高有效位,这是无关紧要的,因为这是一个仅限 BigInt 的类,我们以自己的方式工作左边。但是,如果我们以正确的顺序在上述类的向量的每个元素中存储每个只能是 0-9 的数字,并且我们使用外部 operator+() 函数,这对于一个 BigInt 到另一个来说将是具有挑战性的......例如:
Basic Arithmetic R - L | Vector storing standard
12345 <1,2,3,4,5>
+ 678 <6,7,8>
------
13023
这里 和 的索引不重合,所以这使得很难弄清楚如何将一个有几个数字的值添加到一个有多个数字。我的方法是,如果我们以相反的顺序存储数字:
| Vector stored in reverse
<5,4,3,2,1>
<6,7,8>
那么加法就变得简单了!我们所要做的就是将 BigInt 的两个向量中的每个数字加上相同的索引值。我们可以使用进位数字来结转到下一个字段。生成的 BigInt 返回的大小至少等于或大于两个 BigInt 中最大的一个。如果 carryDigit 有一个值,那么下一次迭代的加法运算将包括 3 个加法而不是 2 个。现在,当获取 BigInt 进行显示时,我们可以返回一个向量>,但当用户获取它时,这不是“BigInt”,它是数字向量,并且它的顺序也是正确的。它甚至可以通过字符串表示形式返回。这个类可以通过一个vector来构造>,在内部存储的时候会颠倒顺序。
这是我的 BigInt 类的总体概念,我只是想知道这是否是一个好的设计计划,是否会被认为是有效的,我想我的主要问题是关于使用什么来存储实际的Digit 类中的数字...std::bitset<> 是否适合节省内存占用空间,或者使用char 会更好,而不用担心额外的空间,因为它更易于实现?
【问题讨论】:
-
这是作为练习还是制作?因为那里有一大堆经过验证的真正的 bigint 库,带有 C++ 接口,例如GMP.
-
如果我要实现自己的大整数,我会使用内置类型(例如 32 位)来保存每个“数字”......本质上意味着我的数字表示是基本的 - 2 ^ 32,而不是你建议的base-10或base-16。我所有的操作都是二进制的,不会浪费空间。所以整个数字就像
std::vector<uint32_t>。这使得 Input/Output 与 base-10 之间的转换稍微复杂一些,但数学运算会很快且可矢量化……对我来说,这就是 big-int 的全部意义。 -
"半个字符会很好,但在 c++ 中不存在..." 它也不存在于汇编程序中,因为它对于设计用于处理 32 位或 64 位的处理器毫无意义一次处理 8 位的内部便利。而
std::bitset不会帮助你,因为它以字大小的块分配存储(至少在 GCC 实现中)。 -
嗯,在我看来,您已经看到了问题,选择了不合适的解决方案,然后对其进行了过度设计。
-
@FrancisCugler - 我同意帕迪的观点。十六进制和十进制的不同类型是非常有缺陷的设计。整数类型的一项要求是能够相互分配和比较它们(例如相等)。您的方法意味着您需要实施的操作数量与您支持的基础数量成二次方 - 难以维护。考虑使用单个类来有效地表示一个无符号的 bigint 值,然后使用该无符号的 bigint 类型和一个
bool来实现一个有符号的 bigint 来表示符号。在输入或输出处处理碱基,而不是在内存中具有不同的表示。
标签: c++ types biginteger template-specialization memory-footprint