项目 2 的任务是实现一个简单的定点数类。类使用整数类型表示定点数。小数点后的位数是一个固定的常量,四。例如,将数字 3.1415 表示为整数 31415,将 3.14 表示为 31400。您必须重载算术、比较和 I/O 运算符来维护定点虚构。
给类命名fixed
。它应该有以下公共成员:
基础整数类型的类型别名,如int
或long
。通过在整个fixed
类中使用value_type
,只需改变value_type
的声明,就可以轻松地在int
和long
之间切换。
A static const int
等于 4,或者小数点后的位数。通过使用命名常量而不是硬编码值 4,您可以很容易地在将来将该值更改为 2 或其他值。
一个static const int
等于 places10
,或者为定点值的比例因子。用内部整数除以places10
得到真值。用一个数字乘以places10
将它缩放成一个整数,这个整数被fixed
对象存储在内部。
默认构造器。
从整数部分和小数部分生成定点值的构造器。例如,要构造定点值 10.0020,请使用fixed{10, 20}
。
如果fraction < 0
抛出std::invalid_argument
。如果是fraction >= places10
,那么构造器应该丢弃右边的数字,舍入结果。比如fixed{3, 14159} == fixed{3, 1416}
和fixed{31, 415926} == fixed{31, 4159}
。
从浮点数生成定点值的构造器。将分数四舍五入,去掉多余的数字。由此,fixed{12.3456789} == fixed{12, 3456789} == fixed{12, 3457}
。
实现算术运算符、算术赋值运算符、比较运算符和 I/O 运算符。不要担心溢出。读取定点数时尽可能检查错误。一定要处理不带小数点的整数(42
)和带太多小数点的值(3.14159
)。
实现一个成员函数,将定点值转换为std::string
。
将该值转换为字符串表示形式;比如 3.1416 变成"3.1416"
,–21 变成"-21.0000"
。
转换成整数意味着丢弃信息。为了让用户非常清楚,调用函数round()
,强调定点值必须四舍五入为整数。
四舍五入到最接近的整数。如果小数部分正好是 5000,则四舍五入到最接近的偶数(银行家四舍五入)。一定要处理负数和正数。
其他有用的成员函数让您可以访问原始值(有利于调试、实现附加操作等)。)或定点值的部分:整数部分和小数部分。
只返回整数部分,不返回小数部分。
只返回小数部分,不返回整数部分。分数部分始终在[ 0
,places10
]范围内。
在一个名为fixed
的模块中实现fixed
类。您可以决定是编写单独的接口和实现模块,还是编写单个模块文件。决定哪些成员函数应该是内联的(如果有的话),并确保在模块接口中定义所有的内联函数。完成后,仔细检查您的解决方案并进行一些测试,将您的结果与我的结果进行比较,您可以从本书的网站上下载。
如果你需要帮助测试你的代码,试着将你的fixed
模块与清单 50-1 中的测试程序链接起来。测试程序使用在test
模块中声明的test
和test_equal
功能。细节超出了本书的范围。用一个布尔参数调用test
。如果参数为真,则测试通过。否则,测试失败,并且test
打印消息。test_equal
函数接受两个参数,如果它们不相等,则打印一条消息。因此,如果程序没有产生输出,所有测试都通过了。
import <iostream>;
import <sstream>;
import <stdexcept>;
import test;
import fixed;
int main()
{
fixed f1{};
test_equal(f1.value(), 0);
test_equal(f1.to_string(), "0.0000");
fixed f2{1};
test_equal(f2.value(), 10000);
test_equal(f2.to_string(), "1.0000");
fixed f3{3, 14162};
test_equal(f3.value(), 31416);
fixed f4{2, 14159265};
test_equal(f4.value(), 21416);
test_equal(f2 + f4, f1 + f3);
test(f2 + f4 <= f1 + f3);
test(f2 + f4 >= f1 + f3);
test(f1 < f2);
test(f1 <= f2);
test(f1 != f2);
test(f2 > f1);
test(f2 >= f1);
test(f2 != f1);
test_equal(f2 + f4, f3 - f1);
test_equal(f2 * f3, f3);
test_equal(f3 / f2, f3);
f4 += f2;
test_equal(f3, f4);
f4 -= f1;
test_equal(f3, f4);
f4 *= f2;
test_equal(f3, f4);
f4 /= f2;
test_equal(f3, f4);
test_equal(-f4, f1 - f4);
test_equal(-(-f4), f4);
--f4;
test_equal(f4 + 1, f3);
f4--;
test_equal(f4 + 2, f3);
++f4;
test_equal(f4 + 1, f3);
f4++;
test_equal(f4, f3);
++f3;
test_equal(++f4, f3);
test_equal(f4--, f3);
test_equal(f4++, --f3);
test_equal(--f4, f3);
test_equal(f4 / f3, f2);
test_equal(f4 - f3, f1);
test_equal(f4.to_string(), "3.1416");
test_equal(f4.integer(), 3);
f4 += fixed{0,4584};
test_equal(f4, 3.6);
test_equal(f4.integer(), 3);
test_equal(f4.round(), 4);
test_equal(f3.integer(), 3);
test_equal((-f3).integer(), -3);
test_equal(f3.fraction(), 1416);
test_equal((-f3).fraction(), 1416);
test_equal(fixed{7,4999}.round(), 7);
test_equal(fixed{7,5000}.round(), 8);
test_equal(fixed{7,5001}.round(), 8);
test_equal(fixed{7,4999}.round(), 7);
test_equal(fixed{8,5000}.round(), 8);
test_equal(fixed{8,5001}.round(), 9);
test_equal(fixed{123,2345500}, fixed(123,2346));
test_equal(fixed{123,2345501}, fixed(123,2346));
test_equal(fixed{123,2345499}, fixed(123,2345));
test_equal(fixed{123,2346500}, fixed(123,2346));
test_equal(fixed{123,2346501}, fixed(123,2347));
test_equal(fixed{123,2346499}, fixed(123,2346));
test_equal(fixed{123,2346400}, fixed(123,2346));
test_equal(fixed{123,2346600}, fixed(123,2347));
test_equal(fixed{-7,4999}.round(), -7);
test_equal(fixed{-7,5000}.round(), -8);
test_equal(fixed{-7,5001}.round(), -8);
test_equal(fixed{-7,4999}.round(), -7);
test_equal(fixed{-8,5000}.round(), -8);
test_equal(fixed{-8,5001}.round(), -9);
test_equal(fixed{-3.14159265}.value(), -31416);
test_equal(fixed{123,456789}.value(), 1234568);
test_equal(fixed{123,4}.value(), 1230004);
test_equal(fixed{-10,1111}.value(), -101111);
std::ostringstream out{};
out << f3 << " 3.14159265 " << fixed(-10,12) << " 3 421.4 end";
fixed f5{};
std::istringstream in{out.str()};
test(in >> f5);
test_equal(f5, f3);
test(in >> f5);
test_equal(f5, f3);
test(in >> f5);
test_equal(f5.value(), -100012);
test(in >> f5);
test_equal(f5.value(), 30000);
test(in >> f5);
test_equal(f5.value(), 4214000);
test(not (in >> f5));
test_equal(fixed{31.4159265}, fixed{31, 4159});
test_equal(fixed{31.41595}, fixed{31, 4160});
bool okay{false};
try {
fixed f6{1, -1};
} catch (std::invalid_argument const&) {
okay = true;
} catch (...) {
}
test(okay);
test_exit();
}
Listing 50-1.Testing the fixed Class
如果你需要一个提示,我实现了fixed
以便它存储一个单一的整数,并从右开始隐含小数点位置places10
。因此,我将值 1 存储为 10000。加减法很容易。当乘或除时,你必须缩放结果。(更好的方法是在乘法之前缩放操作数,这可以避免一些溢出的情况,但是您必须小心不要损失精度。)