如何在Python中找到低精度浮点值的原始文本表示?

2024-05-25

我遇到了显示问题floatPython 中的值,从外部数据源加载
(它们是 32 位浮点数,但这也适用于较低精度的浮点数).

(以防万一,这些值是由人类在 C/C++ 中输入的,因此与任意计算值不同,与round数字很​​可能not预期的,但不能被忽略,因为这些值可能是常量,例如M_PI或乘以常数)。

由于 CPython 使用较高的精度(通常为 64 位),因此作为较低精度浮点数输入的值可能会repr()显示 32 位浮点造成的精度损失,其中 64 位浮点将显示舍入值。

eg:

# Examples of 32bit float's displayed as 64bit floats in CPython.
0.0005 -> 0.0005000000237487257
0.025  -> 0.02500000037252903
0.04   -> 0.03999999910593033
0.05   -> 0.05000000074505806
0.3    -> 0.30000001192092896
0.98   -> 0.9800000190734863
1.2    -> 1.2000000476837158
4096.3 -> 4096.2998046875

在大多数情况下,简单地将值舍入到某个任意精度是可行的,但可能不正确,因为它可能会丢失重要的值,例如:0.00000001.

可以通过打印转换为 32 位浮点的浮点来显示此示例。

def as_float_32(f):
    from struct import pack, unpack
    return unpack("f", pack("f", f))[0]

print(0.025)               #  --> 0.025
print(as_float_32(0.025))  #  --> 0.02500000037252903

所以我的问题是:

获得 32 位浮点的原始表示形式而不做出假设或丢失精度的最有效和最直接的方法是什么?

换句话说,如果我有一个包含 32 位浮点数的数据源,这些数据最初是由人类作为舍入值输入的(上面的示例),但是将它们表示为更高精度的值会暴露出该值作为 32 位浮点数是一个原始值的近似值。

我想扭转这个过程,并得到round从 32 位浮点数据返回数字,但不会失去 32 位浮点给我们的精度。 (这就是为什么简单地舍入不是一个好的选择)。


您可能想要这样做的原因示例:

  • 生成 API 文档,其中 Python 从内部使用单精度浮点数的 C-API 中提取值。
  • 当人们需要读取/查看生成的数据值时,这些数据恰好以单精度浮点数形式提供。

在这两种情况下,重要的是不要失去显着的精度,或者显示人类一眼无法轻松读取的值。


  • 更新,我已经制定了一个解决方案,我将其作为答案包含在内(以供参考并展示其可能性),但高度怀疑它是一个高效或优雅的解决方案。

  • 当然你不可能知道所使用的符号:0.1f, 0.1F or 1e-1f输入的位置,这不是这个问题的目的。


您正在寻求解决与 Python 本质上相同的问题repr解决,即找到舍入为给定浮点数的最短十进制字符串。除了在您的情况下,浮点不是 IEEE 754 二进制 64(“双精度”)浮点,而是 IEEE 754 二进制 32(“单精度”)浮点。

只是为了记录,我当然应该指出检索原始字符串表示是不可能的,因为例如字符串'0.10', '0.1', '1e-1' and '10e-2'全部转换为相同的浮点数(或者在本例中float32)。但在合适的条件下,我们仍然可以希望生成一个与原始字符串具有相同十进制值的字符串,这就是我下面要做的。

您在答案中概述的方法或多或少有效,但可以稍微简化一下。

首先,一些界限:当涉及到单精度浮点数的十进制表示时,有两个幻数:6 and 9。意义6任何具有 6 个或更少有效十进制数字的(不太大、不太小的)十进制数字字符串都将通过单精度 IEEE 754 浮点数正确往返:即将该字符串转换为最接近的值float32,然后转换that值返回到最接近的值6-digit 十进制字符串,将生成一个与原始值相同的字符串。例如:

>>> x = "634278e13"
>>> y = float(np.float32(x))
>>> y
6.342780214942106e+18
>>> "{:.6g}".format(y)
'6.34278e+18'

(这里,“不太大,不太小”只是指下溢和上溢范围float32应该避免。上述属性适用于所有正常值。)

这意味着对于您的问题,如果original字符串有 6 位或更少的数字,我们可以通过简单地将值格式化为 6 位有效数字来恢复它。因此,如果您只关心恢复具有 6 个或更少有效十进制数字的字符串,您可以停止阅读此处:一个简单的'{:.6g}'.format(x)足够。如果您想更普遍地解决问题,请继续阅读。

对于另一个方向的往返,我们有相反的属性:给定任何单精度浮点数x,将该浮点转换为 9 位十进制字符串(一如既往地四舍五入到最接近的值),然后将该字符串转换回单精度浮点,将始终精确地恢复该浮点的值。

>>> x = np.float32(3.14159265358979)
>>> x
3.1415927
>>> np.float32('{:.9g}'.format(x)) == x
True

与你的问题的相关性是always至少一个 9 位数字的字符串四舍五入为x,所以我们永远不必考虑超过 9 位数字。

现在我们可以遵循您在答案中使用的相同方法:首先尝试 6 位数字的字符串,然后是 7 位数字,然后是 8 位数字。如果这些都不起作用,那么根据上述内容,9 位数字字符串肯定会起作用。这是一些代码。

def original_string(x):
    for places in range(6, 10):  # try 6, 7, 8, 9
        s = '{:.{}g}'.format(x, places)
        y = np.float32(s)
        if x == y:
            return s
    # If x was genuinely a float32, we should never get here.
    raise RuntimeError("We should never get here")

示例输出:

>>> original_string(0.02500000037252903)
'0.025'
>>> original_string(0.03999999910593033)
'0.04'
>>> original_string(0.05000000074505806)
'0.05'
>>> original_string(0.30000001192092896)
'0.3'
>>> original_string(0.9800000190734863)
'0.98'

然而,上述内容有几个警告。

  • 首先,为了使我们使用的关键属性成立,我们必须假设np.float32总是这样正确舍入。情况可能是这样,也可能不是,具体取决于操作系统。 (即使在相关操作系统调用声明正确舍入的情况下,仍然可能存在该声明不正确的极端情况。)在实践中,很可能是np.float32足够接近正确舍入,不会引起问题,但为了完全自信,您需要知道它是否正确舍入。

  • 其次,上面的方法不适用于低于正常范围的值(因此对于float32,任何小于2**-126)。在次正常范围内,6 位十进制数字字符串不再能够通过单精度浮点数正确往返。如果你关心次正常,你需要在那里做一些更复杂的事情。

  • 第三,上面有一个非常微妙(而且有趣!)的错误:almost根本不重要。我们使用的字符串格式总是四舍五入x to the nearest places-digit 十进制字符串到真实值x。然而,我们只想知道是否存在any places-digit 十进制字符串四舍五入到x。我们隐含地假设(看似显而易见的)事实:如果有any places- 四舍五入到的十进制字符串x,那么closest places-digit 十进制字符串四舍五入为x。那就是almosttrue:根据以下属性,舍入到的所有实数的间隔x周围对称x。但这种对称性在一种特殊情况下会失效,即当x是一个幂2.

So when x是一个精确的幂2, it's possible(但不太可能)(例如)最接近的 8 位十进制字符串x doesn't舍入到x,但仍然有一个 8 位十进制字符串可以舍入为x。您可以对在范围内发生这种情况的情况进行详尽的搜索float32,事实证明,恰好有三个值x发生这种情况的情况,即x = 2**-96, x = 2**87 and x = 2**90。对于 7 位数字,没有这样的值。 (对于 6 和 9 位数字,这种情况永远不会发生。)让我们仔细看看这个案例x = 2**87:

>>> x = 2.0**87
>>> x
1.5474250491067253e+26

让我们取最接近的 8 位十进制值x:

>>> s = '{:.8g}'.format(x)
>>> s
'1.547425e+26'

事实证明这个值doesn't绕回到x:

>>> np.float32(s) == x
False

但接下来的 8 位十进制字符串却是这样的:

>>> np.float32('1.5474251e+26') == x
True

同样,情况如下x = 2**-96:

>>> x = 2**-96.
>>> x
1.262177448353619e-29
>>> s = '{:.8g}'.format(x)
>>> s
'1.2621774e-29'
>>> np.float32(s) == x
False
>>> np.float32('1.2621775e-29') == x
True

因此,忽略次正规值和溢出,在所有 20 亿左右的正正规单精度值中,正好有three values x上面的代码不起作用。 (注:我原本以为只有一个;感谢 @RickRegan 在评论中指出了错误。)所以这是我们的(有点半开玩笑的)固定代码:

def original_string(x):
    """
    Given a single-precision positive normal value x,
    return the shortest decimal numeric string which produces x.
    """
    # Deal with the three awkward cases.
    if x == 2**-96.:
        return '1.2621775e-29'
    elif x == 2**87:
        return '1.5474251e+26'
    elif x == 2**90:
        return '1.2379401e+27'

    for places in range(6, 10):  # try 6, 7, 8, 9
        s = '{:.{}g}'.format(x, places)
        y = np.float32(s)
        if x == y:
            return s
    # If x was genuinely a float32, we should never get here.
    raise RuntimeError("We should never get here")
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在Python中找到低精度浮点值的原始文本表示? 的相关文章

随机推荐

  • 在 CustomAdapters 中使用条件 if(view==null)

    我正在为 ListView 编写一个自定义适配器 它扩展了 BaseAdapter 并在此方法中 Override public View getView int position View convertView ViewGroup pa
  • document.execCommand('copy') 有大小限制吗?

    我使用的 document execCommand copy 与此处描述的类似 https developers google com web updates 2015 04 cut and copy commands https deve
  • iOS10 iMessage:无法使用 MSConversation 将数据插入 iMessage

    在我的项目中我添加了iMessage Extension但我无法发送选定的数据UITableview 需要发送选定行数据 声明 var savedConversation MSConversation 我想在用户时发送文本didselect
  • 标记处的其他选项

  • 通过 bash 从文件中检索电子邮件主题

    我有一个 shell 脚本 它将文件从服务器邮件文件夹下载到 NAS 设备 以便客户端拥有本地备份的副本 文件保存为11469448248 H15587P19346 smtp x14 eu 2 S文件 我已将扩展名更改为标准 eml 格式
  • 上的 EL 语法错误

    我正在尝试为链接创建一个条件 其中如果长度不 0 则显示描述 但我收到语法错误 代码是
  • dispatch_async 在某个地方有滞后,找不到哪里。 NSLog有问题吗?

    所以我有这个代码 dispatch async dispatch get global queue DISPATCH QUEUE PRIORITY DEFAULT 0 Bunch of code NSLog Test 它立即运行并返回 ns
  • 使用 javascript 将 CSS 类添加到具有另一个类名的所有元素

    我正在尝试使用 javascript 将类添加到具有不同类的所有元素 我知道你可能认为这是多余的 但对于我所处的情况来说这是必要的 我需要一种方法来查看具有该类名称的所有元素并添加该类 但我不明白如何获取计数 我正在使用一个 cms 但我无
  • 当隐含 ID 列时,如何合并 csv 文件中的多个数据框?

    我想将一堆数据框合并在一起 因为如果您只处理一个数据框 则许多操作似乎会更容易 但如果我错了 请纠正我 目前我有一个像这样的数据框 ID var1 var2 A 2 2 B 4 5 Z 3 2 每个 ID 位于单行上 并带有多个单个测量值
  • 在 sympy 绘图中,如何获得具有固定纵横比的绘图?

    如果我用这个片段画一个圆 from sympy import x y symbols x y p1 plot implicit Eq x 2 y 2 1 aspect ratio 1 1 我会得到一个像这样的图形窗口 现在长宽比不是我所期望
  • 帮助重构这个讨厌的 Ruby if/else 语句

    所以我有这个大而多毛的 if else 语句 我将跟踪号码传递给它 然后它确定它是什么类型的跟踪号码 我怎样才能简化这件事 具体来说就是想减少代码行数 if num length lt 8 tracking service false el
  • ODP.NET 可以重新分发吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Oracle ODP NET 库是否可重新分发 例如 我是否可以简单地将 Oracle DataAccess dll 包含在我的应用程序中
  • MySQL/PHP 插入同一行两次

    我不明白为什么这段代码会插入同一行两次 我已经将其精简为以下代码 它被插入的 MySQL 表中有 10 列 但即使查询中提到了所有这些列 它仍然插入 电子邮件受保护 cdn cgi l email protection 分两行 具有单独的主
  • PHP Microsoft Excel 文件生成/导出类

    我一直在寻找一个好的 Excel 文件生成类 但还没有找到 我的首要问题是 虽然我可以在 Excel 中打开导出的文件 运行 2007 年 但我总是收到一条警告 文件的格式与文件扩展名不同 我注意到 phpMyAdmin 中的 Excel
  • Backbone.Marionette 与 Backbone-Boilerplate

    我是 Backbone 的新手 正在尝试决定如何进行开发 目前我想知道人们什么时候会使用backbone marionette而不是backbone boilerplate 据我所知 Marionette 的规范性更强 但这是大多数人进行开
  • React Native - “this.setState 不是一个函数”试图设置背景颜色动画?

    好吧 我只是想循环视图的背景颜色 在 3 4 种颜色之间渐变 我发现如何在 React Native 中对 ScrollView 的背景颜色进行动画处理 https stackoverflow com questions 50356933
  • goJS 下拉菜单删除项目

    我有简单的 python Flask goJS 图形应用程序 如下所示 节点和链接文本的源是从应用程序的后端加载的 我将它们设置为model modelData像这样的部分 var graphDataString JSON parse di
  • 在Linux中的端口80上运行flask[重复]

    这个问题在这里已经有答案了 也许以前有过这个问题的答案 所以请重定向我 如果是这样的话 我正在考虑在端口 80 上运行 Flask 所以我检查了是否有任何东西正在使用端口 80 因为事实证明端口 80 没有运行 所以当我输入以下内容时 if
  • getScript 本地加载而不是全局加载?

    根据我的阅读 JQuery 的 getScript 函数使用名为 global eval 的函数在全局上下文中加载脚本文件 是否有特定的设置或方法可以更改此设置 以便它将改为在我调用它的函数中加载 如果我执行以下代码名称 则返回未定义 因为
  • 如何在Python中找到低精度浮点值的原始文本表示?

    我遇到了显示问题floatPython 中的值 从外部数据源加载 它们是 32 位浮点数 但这也适用于较低精度的浮点数 以防万一 这些值是由人类在 C C 中输入的 因此与任意计算值不同 与round数字很 可能not预期的 但不能被忽略