2

我正在玩弄一个应用程序,粗略地说,它是一种用于建筑行业的建模器应用程序。将来我希望用户可以同时使用 SI 单位和英制单位。据我了解,在美国建筑行业中,在指定测量值时习惯使用英寸的分数,例如 3 1/2" - 而在国际单位制中,我们会写 3.5,而不是 3 1/2。我正在寻找一种方法在我的软件中使用这些不同的系统 - 存储它们,对它们进行计算等,不仅解析用户输入的内容。它应该能够以他输入的方式向用户显示测量,但能够计算其他测量值 - 例如将 3 厘米添加到 1 1/2 英寸。因此,如果用户绘制 5 英尺的墙壁长度和 3 米的另一面,

我还没有决定应该为用户输入数据增加多少灵活性;例如,如果他输入 1 英尺 14 英寸,下次显示测量值时应该是 2 英尺 2 英寸吗?但是,在我决定这样的事情之前,我正在寻找一种以精确形式存储测量值的方法,这就是我的问题所在。

我正在使用 C++ 并且查看了 Boost.Units,但这似乎并没有提供处理分数的方法。

简单的选择是将所有内容转换为毫米,但舍入误差会导致无法返回到用户输入的精确测量值(如果他以英制测量值输入)。所以我需要一些更复杂的东西。

现在我正在使用一个暂时命名为“距离”的类,在概念上看起来像这样:

class Distance
{
public:
    Distance(double value);
    // operators +, -, *, /
    Distance operator+(const Distance& that);
    ...etc...

    std::string StringForm(); // Returns a textual form of the value

    Distance operator=(double value);

private:
    <question: what should go here?>
}

这清楚地表明了我的问题所在。最明显的做法是有一个枚举,说明此距离是存储 SI 单位还是英制单位,并具有存储米、厘米和毫米(如果是 SI 单位)和英尺和英寸(如果这是帝国的。然而,这将使类的实现充满 if(SI) else ...,并且非常浪费内存。另外,例如,我必须存储英尺和英寸的分子和分母才能准确存储 1/3"。

因此,鉴于我的设计要求,我正在寻找关于如何解决这些问题的一般设计建议。当然,如果有一个 C++ 库已经可以做这些事情,或者我可以查看另一种语言的库来复制概念,那就太好了。

4

7 回答 7

5

我肯定会考虑将 Units 属性添加到距离类中。然后,您可以重载 +、-、*、/(和相关的)运算符,以便仅当单位类型相同时才可能对距离进行算术运算。

就个人而言,我会将所有测量值归一化为您将在每个系统中支持的最低测量单位(例如,SI 为毫米,英制为英寸),但也存储用户输入的表示。以标准化形式执行所有计算,但在呈现给用户时转换回更易读的形式。

您还应该考虑使距离的实例不可变 - 并在执行算术运算时创建一个新的距离。

最后,您可以创建辅助方法来在不同单位之间进行转换 - 甚至在对不同单位的距离执行算术时甚至可能在内部调用这些方法。只需将所有内容转换为通用单位,然后执行计算。

就个人而言,我不会走在每个系统中为测量创建多种类型的路线 - 我认为您最好整合逻辑并允许您的系统以多态方式处理测量。

于 2009-07-06T15:35:28.800 回答
5

看看 Martin Fowler 的企业应用程序架构模式中的金钱模式——它直接适用于这种情况。推荐阅读。Fowler 还在他的网站上发布了一篇关于数量模式的简短文章,这是一种更通用的货币版本。

于 2009-07-06T15:40:43.330 回答
1

您是对的,将所有测量类型转换为一种测量类型会给用户带来恼人的舍入错误。

您应该创建一个声明操作的虚拟基类。

您应该为每个要实现的测量系统设置一个具体的子类:例如公制、美制、古罗马。在内部,这可以以最合适和最准确的格式存储它们 - 例如英制的分数英寸。

您将需要每个子类(测量系统)的字符串输出机制。

您应该有一个工厂来将字符串表示形式转换为适当类的实例。

您将需要为每个子类实现操作(例如添加),保留类型,因此英制 + 英制构成英制。

您将需要实现交叉类型操作,并决定如果添加毫米和英寸,您希望发生什么。它应该输出公制还是英制?

-亚历克斯

于 2009-07-06T15:33:26.837 回答
1

美国测量(有些人称其为“英制”或“英制”单位;我们喜欢将我们的错误归咎于他人)要求您使用有理数包来存储英寸。

对于公制,您有几个具有简单关系的不同单位(cm、m、km 等)。将它们视为具有简单转换因子的独立单元。m 到 cm 是 *100 的转换。您可以轻松枚举所有可能的公制距离度量中的所有 *100、*1000、*.1、*.01 转换因子。

对于英语,单位(英寸、英尺、码等)也有简单的关系。他们只是不迷恋小数点。使用 12 和 3 而不是 10。将它们视为具有简单转换因子的独立单元。ft 到 in 是 *12 转换。同样,您可以轻松枚举所有 *12、*36、*(1/12)、*(1/36) 组合。

当有人输入 3' 8" 时,您可以将其标准化为英寸并将其正确转换回来。即使他们输入 3' 14",您转换回 4' 2" 也是正确且预期的。在某些情况下,这是需要的。

在某些情况下——即使是英文符号——有一个不是原始输入单元的期望输出单元。例如,某人可能有一长串以英尺和英寸为单位的测量值,但想要以十进制英尺为单位的总和。当您批量购买时,您不在乎它是 25' 6 7/16";25.54' 是一个很好的答案。您购买的是 26' 的木材,这通常意味着 3 个 10' 的木板。

事实证明,这种单位转换也适用于 m 到 ft 和返回。您可以枚举 in、ft、yd、mm、cm、m、km 转换的每种组合。输入单位被标准化为短的(厘米,英寸)并在输出时转换为所需的单位(米,英尺,码等)

您可以存储 1356 英寸。没关系。您可以显示 113' 或 37.66 码,或 37 码 2 英尺,具体取决于用户选择的输出单位。

该方案适用于除温度以外的任何情况。

唯一的障碍是几分之一英寸。做到这一点需要一个 Rational number 包。你想要的是这样的类层次结构。

Distance
|
+---- Float (no fractions, everything but inches)
|
+---- Rational (fractions used for inches)

Rational 测量值采用用户提供的分数表示法。如果您的 Rational 类正确地覆盖了所有运算符,那么它的工作方式与浮点测量相同。如果它提供了在 int 和 float 之间转换的适当函数,您应该能够将两者混合并获得合理的答案而无需太多 RTTI。


这就是奇怪的地方。英寸使用 2 的幂,并具有精确的浮点表示。没有 24.000000001 或其他转换工件。公制使用 10 的幂,因此您会得到各种看起来很傻的 24.00000001 和 23.99999997 公制计算答案。

于 2009-07-06T15:51:49.740 回答
1

我认为美国宇航局曾经以这种方式丢失了火星探测器。

“因此,如果用户绘制了 5 英尺的墙壁长度和 3 米的另一面,则总测量值应显示在……”</p>

很难想象这样做的用户期望发生什么。除非我完全确定我知道用户为什么这样做,以及预期会发生什么,否则我只会拒绝它。如果选择 SI 单位,则不允许使用英制单位,反之亦然。在大多数情况下,我认为这是帮助用户并防止不断扩大的混乱传播的最佳方式。

当使用英制单位时,我会在内部以 1/32 英寸为单位存储距离,转换为输出/输入所需的英尺、英寸和分数。这将避免舍入错误和从二进制表示的十进制值和分数转换的所有讨厌的问题。

并不是说在建筑行业中,“2 x 4”实际上并不是 2 x 4 英寸。

“但是制定一个公制计划并以英制导入 Sketchup 模型并不是牵强附会。我同意混合测量系统是一个坏主意,但我发现在现有的软件包中非常令人沮丧,因为它们将选择限制在一个或另一个,尤其是当您在制作新文件/计划时只能选择一个时。”

是的。导入和转换文件或组件库的单位是程序非常有用的重要且重要的功能。这意味着将所有测量值从一个系统转换到另一个系统。必须决定如何做到这一点(向上四舍五入到最接近的 1/4" 或 1/8" 等)必须实施、强制执行这些决定,并可能将其纳入用户选项。这很复杂,很难做到正确。因此,复杂性和不稳定性应该被限制在一个单独的模块中,它只做一件事:转换一个文件或一组文件中的单元。程序的其余部分应使用一致的单位集,而不是以零碎的方式从一个转换为另一个。恕我直言

于 2009-07-06T16:07:52.970 回答
0

我建议使用Decimal类型而不是Double类型。它速度较慢,但​​速度差异不会影响您。

至于舍入问题,只需要显示的最大位数,但在内部存储完整的数字。即使您将 1 英尺存储为 0.305 米(1.00065617 英尺),用户在显示时也不会知道您是否将其四舍五入到最接近的 100 分之一。

作为对 Roel 的回复:你很可能是低的,因为你是高的(而且,它是 0.006,而不是 0.06)。但是,如果像这样连续有 1000 个相同的测量值并且相差0.06 英尺,那么它将是 100.06 英尺而不是 100 英尺。这可能建筑业很重要。但是,我使用的有效数字远少于Decimal数据类型实际提供的有效数字,因为我演示的是舍入问题而不是精度问题。在实践中,我认为公差是这样的,以至于它们会被尝试建造某些东西所固有的不准确所超越(例如,计算可能会偏离小于 1 nm 的微小数字,但起重机的公差可能比 100 nm 差得多)。

于 2009-07-06T15:44:46.633 回答
0

实际上在同一张施工图中有两个测量系统是真实的。在这种情况下,我会将 3 米的墙显示为 3 米,将 5 英尺的墙显示为 5 英尺。

于 2013-03-17T16:10:40.473 回答