Nic*_*ick 11 mysql database-design
我正在处理的应用程序需要存储格式的权重X pounds, y.y ounces.数据库是MySQL,但我想这是数据库不可知的.
我可以想到三种方法:
我认为#1不是一个好主意,因为十进制磅将产生任意精度的数字,这需要存储为浮点数,这可能导致浮点数固有的不准确性.
是否有令人信服的理由选择#2而不是#3或反之亦然?
egg*_*yal 31
TL; DR
选择选项#1或选项#2 - 它们之间没有区别.不要使用选项#3,因为使用它很尴尬.
您声称浮点数存在固有的不准确性.我认为这应该先探索一下.
在决定用于表示数字的数字系统时(无论是在纸上,在计算机电路中还是在其他地方),需要考虑两个单独的问题:
它的基础 ; 和
它的格式.
受有限空间的限制,人们不能代表无限集的任意成员. 例如:无论您购买多少纸张或手写的数量多少,总是有可能找到一个不适合给定空间的整数(您可以在纸张用完之前保持附加额外的数字).因此,对于整数,我们通常将有限空间限制为仅代表那些落在某个特定区间内的空间 - 例如,如果我们有正/负符号和三位数的空间,我们可能会将自己局限于区间[-999,+999].
每个非空间隔包含一组无穷大的实数. 换句话说,不管采取什么样的间隔一个以上的实数等,只要能[-999,+999],[0,1],[0.000001,0.000002]或其他任何东西,还有一组无限雷亚尔的那个区间内(一个只需要保持附加(非零)的小数位数)!因此,任意实数必须始终 "舍入"到可以在有限空间中表示的东西.
可以在有限空间中表示的一组实数取决于所使用的数字系统. 在我们(熟悉的)位置 基10系统中,有限空间足够一半()但不足三分之一(); 相比之下,在(不太熟悉的)位置base-9系统中,它是相反的(分别是相同的数字和).所有这一切的结果是,一些数字只能在位置基数10中使用少量空间来表示(因此对我们人类来说似乎非常"圆"),例如十分之一,实际上需要无限二进制精确存储的电路(因此对我们的数字朋友来说似乎不是很"圆")!值得注意的是,由于2是因子10,反过来也是如此:可以用有限二进制表示的任何数字也可以用有限小数表示.0.5100.33333…100.44444…90.39
对于连续数量,我们不能做得更好. 最终这样的数量必须在某些数字系统中使用有限的表示:无论系统在计算机电路,人类手指,其他东西上是否容易,或者根本没有任何系统,它是任意的,无论使用哪种系统,该值必须是四舍五入的.因此它总是导致"表示错误".
换句话说,即使有一个完全精确的测量仪器(在物理上是不可能的),那么它报告的任何测量都已经四舍五入到恰好适合其显示器的数字(无论它使用什么基数 - 通常为十进制,原因很明显).因此,"86.2盎司"实际上从来不是" 86.2盎司 ",而是" 86.1500000 ...盎司和86.2499999 ...盎司之间的东西 "的表示.(实际上,因为实际上仪器是不完美的,我们真正可以说的是,我们对实际值落在该区间内有一定程度的信心 - 但这肯定是从某个方面偏离了这一点).
但我们可以为离散量做得更好.这些值不是"任意实数",因此以上都不适用于它们:它们可以精确地表示在它们被定义的数字系统中 - 实际上应该是(转换为另一个数字系统并截断为有限长度会导致四舍五入到不精确的数字).计算机可以(低效率)通过将数字表示为字符串来处理这种情况:例如,考虑ASCII或BCD编码.
由于它是数字系统(有些任意)基础的属性,因此值是否为"圆形"与其精度无关.这是一个非常重要的观察,与许多人的直觉背道而驰(这也是我花了这么多时间解释数字基础的原因).
精确度取决于表示有多少重要数字.我们需要一种能够将我们的值记录到至少与我们认为正确无关的重要数字的存储格式.通过示例值表示我们认为正确的值,86.2并且0.0000862,两个最常见的选项是:
固定点,其中有效数字的数量取决于幅度:例如,在固定的5小数点表示中,我们的值将被存储为86.20000和0.00009(因此分别具有7和1个有效精度数字).在这个例子中,精确度已经在后一个值中丢失了(实际上,我们完全无法表示任何重要的东西,这不会花费太多时间); 并且前一个值存储了假精度,这是对我们有限空间的浪费(实际上,它不会花费太多的时间来使值变得如此之大以至于溢出存储容量).
这种格式适用的一个常见例子是会计系统:货币总和通常必须跟踪一分钱而不管其大小(因此小值需要较低的精度,大值需要更高的精度).实际上,货币通常也被认为是离散的(便士是不可分割的),因此这也是一个很好的例子,其中特定基础(大多数现代货币的十进制)是理想的,以避免上面讨论的表示错误.
人们通常通过将一个值作为公数分母上的商并将分子存储为整数来实现定点存储.在我们的例子中,共同的分母可能是10 5,所以代替
86.20000和0.00009一个将存储整数8620000,9并记住它们必须被除以100000.
浮点,其中有效数字的数量是恒定的,与幅度无关:例如,在5有效数字十进制表示中,我们的值将被存储为86.200和0.000086200(并且根据定义,两次都有5个有效精度数字).在这个例子中,两个值都已存储而没有任何精度损失 ; 并且它们都具有相同数量的错误精度,这样可以减少浪费(因此我们可以使用有限空间来表示更大范围的值 - 无论大小).
这种格式适用的一个常见例子是记录任何现实世界的测量结果:测量仪器的精度(均受系统误差和随机误差影响)是相当稳定的,无论尺度如何,给定足够的有效数字(通常约为3)或4位数),即使基数的变化导致四舍五入到不同的数字,也绝对不会丢失精度.
一个人通常通过将一个值作为带有整数指数的整数有效数来处理浮点存储.在我们的例子中,有效数可以是
86200两个值,其中(base-10)指数分别是-4和-9.
但是我们的计算机使用的浮点存储格式有多精确?
一个IEEE754 单精度(binary32)浮点数有24位,或(超过7)数字,的意义-即,它具有小于公差.换句话说,它比说"更精确".log10(224)±0.000006%86.20000
IEEE754 双精度(二进制64)浮点数具有53位或(几乎16 位)有效值 - 即它具有刚刚超过的容差.换句话说,它比说"更精确".log10(253)±0.00000000000001%86.2000000000000
要认识到的最重要的是,这些格式,分别比10000和超过万亿次更精确的比说"86.2" -即使虽然二回小数的确切转换恰好包括错误虚假的精确(这是我们必须忽略:更多关于这一点)!
还要注意的是两个固定和浮点格式将导致精度的损失,当值更精确地比格式支持公知的. 这种舍入误差可以在算术运算中传播,产生明显错误的结果(这无疑解释了你对浮点数的"固有不准确性"的引用):例如,在5位固定点会产生而不是; 并且在5个有效数字浮点数上会产生而不是.1⁄3 × 3000999.990001000.000001⁄7 − 7⁄500.00286000.0028571
数值分析领域致力于理解这些影响,但重要的是要认识到任何可用的系统(甚至在你脑中进行计算)都容易受到这些问题的影响,因为没有任何保证终止的计算方法可以提供无限的精度.例如,考虑如何计算圆的面积 - 用于π的值必然会丢失精度,这将传播到结果中.
真实世界的测量应该使用二进制浮点:它快速,紧凑,非常精确,并且不比其他任何东西差(包括你开始的十进制版本).由于MySQL的浮点数据类型是IEEE754,这正是它们提供的.
货币申请应该使用否定的固定点:虽然它很慢并且浪费内存,但它确保这两个值都不会四舍五入到不精确的数量,并且这些便士不会在大额货币金额上丢失.由于MySQL的定点数据类型是BCD编码的字符串,这正是它们提供的.
最后,请记住编程语言通常使用二进制浮点类型表示小数值:因此,如果您的数据库以其他格式存储值,则需要小心它们如何被带入您的应用程序,否则它们可能会被转换(所有界面上随之而来的问题.
希望我已经说服你,你的值可以安全地(并且应该)存储在浮点类型中,而不必担心任何"不准确"?记住,它们比你脆弱的3位有效数字十进制表示更精确:你只需要忽略错误的精确度(但是无论如何,即使使用定点十进制格式,也必须始终这样做).
至于你的问题:在选项3中选择选项1或2 - 它使比较更容易(例如,找到最大质量,可以使用MAX(mass),而在两列中有效地进行比较需要一些嵌套).
在这两者之间,无论选择哪个 - 浮点数都存储有恒定数量的有效位而与其规模无关.
此外,虽然在一般情况下,可能会发生某些值被舍入为使用选项1更接近其原始十进制表示的二进制数,而其他值则使用选项2舍入为更接近其原始十进制表示的二进制数,如我们很快就会看到这种表示错误只会出现在应该总是被忽略的错误精度内.
但是,在这种情况下,因为它有16盎司到1磅(16是2的幂),使用这两种方法的原始十进制值和存储的二进制数之间的相对差异是相同的:
5.387510(不是你的问题中所述)将存储在binary32 float中(这是):这是来自原始值(但是,如上所述,"原始值"已经是一个非常糟糕的物理量表示它代表).5.3367187510101.01100011001100110011025.38749980926513671875100.0000036%
既知道binary32浮动店仅7精度十进制数字,我们的编译器知道肯定,一切从8位数开始是绝对错误的精度,因此必须在被忽略每一个案件,因此,只要我们输入值并不需要更多的精度比那个(如果确实如此,binary32显然是错误的格式选择),这保证了返回一个十进制值,看起来就像我们开始的那样圆:.但是,我们应该在这一点上真正应用领域知识(我们应该使用任何存储格式)来丢弃可能存在的任何进一步的错误精度,例如那两个尾随零.5.38750010
86.210将存储在binary32 float中(即):这也是原始值.和以前一样,我们忽略false precision返回原始输入.1010110.00110011001100110286.1999969482421875100.0000036%
注意数字的二进制表示是如何相同的,除了小数点的位置(相隔四位):
101.0110 00110011001100110 101 0110.00110011001100110
这是因为5.3875×2 4 = 86.2.
除此之外:作为欧洲人(虽然是英国人),我也对帝国的衡量单位有强烈的厌恶 - 处理不同规模的价值就是如此混乱.我几乎肯定会以SI单位(例如千克或克)存储质量,然后根据我的应用程序的表示层中的要求执行转换为英制单位.加上严格遵守SI单位可能有一天会让你失去1.25亿美元.
我很想将它存储在公制单位中,因为它们往往是简单的小数,而不是像磅和盎司这样的复杂值.这样,你可以存储一个值(即103.25千克)而不是磅 - 盎司当量,并且更容易执行转换.
这是我过去处理过的事情.我在职业摔跤和混合武术(MMA)网站上做了很多工作,需要记录战斗机的高度和重量.它们往往显示为英尺和英寸以及磅和盎司,但我仍然将值存储在厘米和千克当量中,然后在网站上显示时进行转换.