如何避免浮点算术问题? [英] How to avoid floating point arithmetics issues?

查看:93
本文介绍了如何避免浮点算术问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用浮点数时,Python(以及几乎其他所有东西)都有已知的局限性(提供了很好的概述

Python (and almost anything else) has known limitations while working with floating point numbers (nice overview provided here).

尽管在文档中对问题进行了很好的描述,但是它避免了提供任何解决方法.带着这个问题,我试图找到一种或多或少可靠的方法来避免出现以下情况:

While problem is described well in the documentation it avoids providing any approach to fixing it. And with this question I am seeking to find a more or less robust way to avoid situations like the following:

print(math.floor(0.09/0.015))   # >> 6
print(math.floor(0.009/0.0015)) # >> 5

print(99.99-99.973) # >> 0.016999999999825377
print(.99-.973)     # >> 0.017000000000000015

var = 0.009
step = 0.0015
print(var < math.floor(var/step)*step+step) # False
print(var < (math.floor(var/step)+1)*step)  # True

与此问题中建议的不同,它们的解决方案没有帮助解决诸如下一轮和平代码随机失败之类的问题:

And unlike suggested in this question, their solution does not help to fix a problem like next peace of code failing randomly:

  total_bins = math.ceil((data_max - data_min) / width)  # round to upper
  new_max = data_min + total_bins * width
  assert new_max >= data_max 
  # fails. because for example 1.9459999999999997 < 1.946

推荐答案

如果您处理的是离散数量,请使用int.

有时候人们在绝对不应该使用的地方使用float.如果您要计算的是某物(例如世界上的汽车数量)而不是测量的物(例如每天使用多少汽油),则浮点数可能是错误的选择.货币是经常滥用浮点数的另一个示例:如果将银行帐户余额存储在数据库中,则实际上不是123.45美元,而是12345美分. (但另请参见以下有关Decimal的信息.)

If you deal in discrete quantities, use int.

Sometimes people use float in places where they definitely shouldn't. If you're counting something (like number of cars in the world) as opposed to measuring something (like how much gasoline is used per day), floating-point is probably the wrong choice. Currency is another example where floating point numbers are often abused: if you're storing your bank account balance in a database, it's really not 123.45 dollars, it's 12345 cents. (But also see below about Decimal.)

浮点数是通用的.它们非常准确;它们只是不能表示某些分数,例如有限的十进制数字不能表示数字1/3.浮点数通常适用于测量具有误差条的任何类型的模拟量:长度,质量,频率,能量-如果不确定性在2 ^(-52)或更大的数量级,则可能没有 good 不使用float的原因.

Floating-point numbers are general-purpose. They're extremely accurate; they just can't represent certain fractions, like finite decimal numbers can't represent the number 1/3. Floats are generally suited for any kind of analog quantity where the measurement has error bars: length, mass, frequency, energy -- if there's uncertainty on the order of 2^(-52) or greater, there's probably no good reason not to use float.

这个数字看起来很奇怪"是不使用float原因.但这并不意味着您必须以任意精度显示数字.如果一个只有三位有效数字的数字出现在19.99909997918947上,请将其格式化为小数点后一位.

"This number looks weird" is a bad reason not to use float. But that doesn't mean you have to display the number to arbitrary precision. If a number with only three significant figures comes out to 19.99909997918947, format it to one decimal place and be done with it.

>>> print('{:0.1f}'.format(e**pi - pi))
20.0

如果您需要精确的十进制表示形式,请使用Decimal.

Sraw的答案指的是decimal模块,它是标准库的一部分.我已经提到货币是离散量,但是您可能需要对并非所有数字都是离散的货币量进行计算,例如计算利息.如果您正在为会计系统编写代码,则将有一些规则说明何时应用舍入以及各种计算的精确度,这些规格将以小数位表示.在这种情况下以及问题说明固有的十进制表示形式的其他情况下,您将要使用十进制类型.

If you need precise decimal representation, use Decimal.

Sraw's answer refers to the decimal module, which is part of the standard library. I already mentioned currency as a discrete quantity, but you may need to do calculations on amounts of currency in which not all numbers are discrete, for example calculating interest. If you're writing code for an accounting system, there will be rules that say when rounding is applied and to what accuracy various calculations are done, and those specifications will be written in terms of decimal places. In this situation and others where the decimal representation is inherent to the problem specification, you'll want to use a decimal type.

>>> from decimal import Decimal
>>> rate = Decimal('0.0345')
>>> principal = Decimal('3412.65')
>>> interest = rate*principal
>>> interest
Decimal('117.736425')
>>> interest.quantize(Decimal('0.01'))
Decimal('117.74')

但最重要的是,使用在上下文中有意义的数据类型和操作.

您的几个示例都使用math.floor,它取一个float并切掉小数部分.在任何应该使用math.floor的情况下,浮点错误都无关紧要. (如果要舍入到最接近的整数,请改用round.)是的,从数学的角度来看,存在使用浮点运算的方法会导致错误结果的方法.但是现实世界中的数量通常属于以下类别之一:

But most importantly, use data types and operations that make sense in context.

Several of your examples use math.floor, which takes a float and chops off the fractional part. In any situation where you should use math.floor, floating-point error doesn't matter. (If you want to round to the nearest integer, use round instead.) Yes, there are ways to use floating-point operations that have wrong results from a mathematical standpoint. But real-world quantities usually fall into one of these categories:

  1. 完全正确,因此不应放入float;
  2. 精确度远远超过浮点误差的可能累积.

作为一名程序员,知道您要处理的数量并选择适当的数据类型是您工作的一部分.因此,没有浮点数的修正",因为实际上没有问题",只是人们在错误的事情上使用了错误的类型.

As a programmer, it's part of your job to know the quantities you're dealing with and choose appropriate data types. So there's no "fix" for floating point numbers, because there's no "problem" really -- just people using the wrong type for the wrong thing.

这篇关于如何避免浮点算术问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆