Skip to content

Latest commit

 

History

History
198 lines (150 loc) · 7.2 KB

File metadata and controls

198 lines (150 loc) · 7.2 KB

五十、项目 2:定点数

项目 2 的任务是实现一个简单的定点数类。类使用整数类型表示定点数。小数点后的位数是一个固定的常量,四。例如,将数字 3.1415 表示为整数 31415,将 3.14 表示为 31400。您必须重载算术、比较和 I/O 运算符来维护定点虚构。

fixed

给类命名fixed。它应该有以下公共成员:

value_type

基础整数类型的类型别名,如intlong。通过在整个fixed类中使用value_type,只需改变value_type的声明,就可以轻松地在intlong之间切换。

places

A static const int等于 4,或者小数点后的位数。通过使用命名常量而不是硬编码值 4,您可以很容易地在将来将该值更改为 2 或其他值。

places10

一个static const int等于 places10 ,或者为定点值的比例因子。用内部整数除以places10得到真值。用一个数字乘以places10将它缩放成一个整数,这个整数被fixed对象存储在内部。

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(double val)

从浮点数生成定点值的构造器。将分数四舍五入,去掉多余的数字。由此,fixed{12.3456789} == fixed{12, 3456789} == fixed{12, 3457}

实现算术运算符、算术赋值运算符、比较运算符和 I/O 运算符。不要担心溢出。读取定点数时尽可能检查错误。一定要处理不带小数点的整数(42)和带太多小数点的值(3.14159)。

实现一个成员函数,将定点值转换为std::string

to_string()

将该值转换为字符串表示形式;比如 3.1416 变成"3.1416",–21 变成"-21.0000"

转换成整数意味着丢弃信息。为了让用户非常清楚,调用函数round(),强调定点值必须四舍五入为整数。

round()

四舍五入到最接近的整数。如果小数部分正好是 5000,则四舍五入到最接近的偶数(银行家四舍五入)。一定要处理负数和正数。

其他有用的成员函数让您可以访问原始值(有利于调试、实现附加操作等)。)或定点值的部分:整数部分和小数部分。

integer()

只返回整数部分,不返回小数部分。

fraction()

只返回小数部分,不返回整数部分。分数部分始终在[ 0places10 ]范围内。

在一个名为fixed的模块中实现fixed类。您可以决定是编写单独的接口和实现模块,还是编写单个模块文件。决定哪些成员函数应该是内联的(如果有的话),并确保在模块接口中定义所有的内联函数。完成后,仔细检查您的解决方案并进行一些测试,将您的结果与我的结果进行比较,您可以从本书的网站上下载。

如果你需要帮助测试你的代码,试着将你的fixed模块与清单 50-1 中的测试程序链接起来。测试程序使用在test模块中声明的testtest_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。加减法很容易。当乘或除时,你必须缩放结果。(更好的方法是在乘法之前缩放操作数,这可以避免一些溢出的情况,但是您必须小心不要损失精度。)