RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 690856
Accepted
Qwertiy
Qwertiy
Asked:2020-07-11 18:16:49 +0000 UTC2020-07-11 18:16:49 +0000 UTC 2020-07-11 18:16:49 +0000 UTC

号码显示 9223372036854775807

  • 772

为什么不同的语言显示数字不同9223372036854775807,即使它们都使用相同的 8 字节双精度格式来表示数字?

9223372036854775807- 在代码中
9223372036854775808- C++ http://ideone.com/PV5iPg和http://codepad.org/vhQzDMqT
9223372036854776000 - Javascript https://jsfiddle.net/5ugL4rqh/
9223372036854776000 - Java http://ideone.com/QtXRWi
9223372036854780000 - C# http http://ideone.com/36Lzzi

javascript
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    jfs
    2020-07-12T08:20:40Z2020-07-12T08:20:40Z

    在每种环境/语言中,这里有两种转换:

    1. 从源代码中的常量到内存中的对象
    2. 以选定的方式打印此内存对象。

    C++

    C++ 问题中代码的影响:volatile double x = 9223372036854775807.;类似于 gcc 的-ffloat-store选项,让您忘记 可能的额外位,只考虑相关实现中使用的 64 位 IEEE 754 双精度数(IEEE 754 是可选的,但浮点数的具体实现必须记录在案)。

    9223372036854775807.源代码中 的常量被转换为9223372036854775808.双精度(正如预期的这种类型,请参见下面的位表示演示)。同样的事情发生在 CPython 中:

    >>> 9223372036854775807. .as_integer_ratio()
    (9223372036854775808, 1)
    >>> 9223372036854775807. .hex()
    '0x1.0000000000000p+63'
    

    也就是说,9223372036854775807.不能在 IEEE 754 double 中精确表示,因此使用了近似值9223372036854775808. (2 63 ),在这种情况下已经通过以下方式准确推断:cout << fixed << x;作为 ascii 字符串:( "9223372036854775808.000000"在 C 语言环境中)。

    如何在内存中表示 double 并在 IEEE 754 中表示为位,以及如何在 C 中进行打印,在问题printf 作为在C中打印变量的方法的答案中有详细说明。

    在这种情况下,由于数字是 2 的幂,因此很容易找到它的 IEEE 754 表示形式:

    d = ±符号 (1 + 尾数 / 2 52 )二阶 − 1023

    • знак新位为零,因为数字是正数
    • порядок= (63 + 1023) 10 = 10000111110 2得到 2 63
    • 所有显式 52 位均为零(最高мантисса隐式第 53 位始终为 1)

    一个数的所有位在一起:

    0 10000111110 0000000000000000000000000000000000000000000000000000
    

    Python 中的计算证实了这一点:

    >>> import struct
    >>> struct.pack('>d', 9223372036854775808.0).hex()
    43e0000000000000
    >>> bin(struct.unpack('>Q', struct.pack('>d', 9223372036854775808.0))[0])[2:].zfill(64)
    '0100001111100000000000000000000000000000000000000000000000000000'
    

    反过来:

    >>> b64 = 0b0_10000111110_0000000000000000000000000000000000000000000000000000 .to_bytes(8, 'big')
    >>> b64.hex()
    '43e0000000000000'
    >>> "%f" % struct.unpack('>d', b64)[0]
    '9223372036854775808.000000'
    

    示例中数字在内存中的字节顺序显示为从高到低(big-endian),但实际上可以是从低到高(little-endian):

    >>> struct.pack('d', 9223372036854775808.0).hex()
    '000000000000e043'
    

    您可以通过向尾数减去/添加一位来看到可表示的数字不在一行中:

    >>> x = 0b0_10000111110_0000000000000000000000000000000000000000000000000000
    
    >>> def to_float_string(bits):
    ...     return "%f" % struct.unpack('>d', bits.to_bytes(8, 'big'))[0]
    
    >>> for n in range(x-1, x+2):
    ...     print(to_float_string(n))
    9223372036854774784.000000
    9223372036854775808.000000
    9223372036854777856.000000
    

    对于这种数量级的数字,一位的差异会导致十进制表示形式的差异超过一千:.. 4784, .. 5808, .. 7856。

    您可以使用C99 函数nextafter():

    #include <float.h>
    #include <math.h>
    #include <stdio.h>
    
    int main(void)
    {
      volatile double x = 9223372036854775808.0;
      printf("%f\n", nextafter(x, DBL_MIN));
      printf("%f\n", x);
      printf("%f\n", nextafter(x, DBL_MAX));
    }
    

    结果与之前相同:

    9223372036854774784.000000
    9223372036854775808.000000
    9223372036854777856.000000
    

    javascript

    JavaScript 中的数字以一种有趣的方式表示 - 整数表示为 IEEE 754 双精度数。例如,最大数字 ( Number.MAX_SAFE_INTEGER) 是 2 53。

    9223372036854775807大了三个数量级,MAX_SAFE_INTEGER所以不能保证n和n+1是可表示的。

    > 9223372036854776000 === 9223372036854775807
    true
    > 9223372036854775808 === 9223372036854775807
    true
    

    9223372036854776000 (导致document.write(9223372036854775807)其中一个 javascript 实现)被 标准允许作为字符串表示形式9223372036854775807(它仍然是一个 binary64 数字:)0x1.0000000000000p+63。

    按位运算的结果一般限于32 位有符号数。您可以查看要重现用 javascript 实现的散列函数的结果必须采用哪些技巧:如何将字符串散列函数从 Javascript 转换为 Python。

    爪哇

    在 Java中,double 是一种类型,其值包括 64 位 IEEE 754 浮点数。

    double x = 9223372036854775807.;
    System.out.format("%f", x);
    

    为什么为 binary64 数字9223372036854776000选择, 而不是 9223372036854775808.十进制表示的可能逻辑是,通常它允许为小数打印更少的数字 - 不显示尾随零(这是推测 - 我没有深入研究这个问题)。0x1.0000000000000p+63

    C#

    msdn 指出 C# 中的 double 符合 IEEE 754。

    9223372036854780000.0提示它Console.WriteLine("{0:0.0}", x);在打印时四舍五入到 15 位数字。打印的数字不同于x:

    >>> 9223372036854780000. .hex()
    '0x1.0000000000002p+63'
    

    这可能出于类似的原因而发生,打印时0.1显示为0.1,而不是0.10000000000000001或通常显示为0.1000000000000000055511151231257827021181583404541015625( 0.1 .hex() == '0x1.999999999999ap-4')。此外,binary64 表示不同(2 63与 2 63 +2 12)(问题中提供的示例中唯一一个默认情况下不输出等效表示的示例)。

    也许优先级是四舍五入到 15 位数字,不管这是否足以获得等效的binary64 表示。不仅 C# 有这种行为,例如,numpy.array在 Python 中它默认显示 8 位数字:

    >>> import numpy
    >>> a = numpy.array([2**63-1, 2**63, 2**63+2**12], dtype=numpy.float64)
    >>> a
    array([  9.22337204e+18,   9.22337204e+18,   9.22337204e+18])
    >>> 9.22337204e+18 .hex()
    '0x1.0000000176f0ap+63'
    >>> numpy.set_printoptions(precision=16)
    >>> a
    array([  9.2233720368547758e+18,   9.2233720368547758e+18,
             9.2233720368547799e+18])
    

    使用标准 "{0:R}"格式在 C# 中显示 17 位数字是可能 的,它输出 9.2233720368547758E+18,即再次收到相同的原始 2 63功率:

    >>> 9.2233720368547758E+18 .hex()
    '0x1.0000000000000p+63'
    
    • 43
  2. AnT stands with Russia
    2020-07-11T18:45:21Z2020-07-11T18:45:21Z

    double邻域中以 IEEE 754 格式表示的序号9223372036854775807是

    9223372036854774784
    9223372036854775808
    9223372036854777856
    

    最接近的是9223372036854775808。

    并且输出的差异仅出现在形成 IEEE 754 二进制浮点数的十进制表示的阶段。在内部,上述所有语言实现都使用 IEEE 754 double,它在所有情况下都是9223372036854775808,可以通过从存储的数字中减去其最高有效位来轻松验证。

    例如,在您的 C# 示例中,只需x打印而不是x - 9223000000000000000“丢失”的低位数字将立即“出现”的方式就足够了:http: //ideone.com/H5eKPe

    完全相同的事情发生在 Javascript 中。

    • 22
  3. KoVadim
    2020-07-11T18:29:06Z2020-07-11T18:29:06Z

    为什么 0.1 + 0.1 并不总是恰好是 0.2 有点令人费解。如果您查看维基百科,就会清楚双精度数有52 位的螳螂。52 位给出 15-16 位数字。而你还有更多。

    更新程序

    我找到了一个很棒的查看双精度数字的服务http://www.binaryconvert.com/convert_double.html

    我们将9223372036854775807和9223372036854775808驱动到其中,甚至驱动9223372036854775809并看到它们都具有相同的二进制表示 - 0x43E0000000000000。它的反向转换给出9.223372036854775808E18。也就是说,这种格式无法区分三个给定的数字。这回答了问题的第一部分。

    为什么不同的语言对这个数字的显示方式不同?首先,每种语言都使用某种默认系统进行输出格式化。我们不知道语言/编译器/平台的开发人员在那一刻到底在吃什么/喝什么。其次,每个开发人员都试图制作自己的、唯一真实和正确的解析器-转换器。

    让我们看看 java 输出并将其传递给转换器9223372036854776000 => 0x43E0000000000000 - 我们看到相同的表示。显然java算法是不同的。它显然也被 javascript 使用(的确,并不是所有的事情都是显而易见的 - 有许多不同的实现,但我在 visible / Linux 上测试的所有选项都给出了相同的结果)。

    但是夏普这里好像有个bug:9223372036854780000 => 0x43E0000000000002(最后两个)。但后来他们显然纠正了自己并引入了一种特殊的格式 -往返格式

    Console.WriteLine("{0:R}", x);
    

    在这种情况下,我们有 9.2233720368547758E+18,相当于 9223372036854775800。在二进制表示中,它将是相同的 0x43E0000000000000。不可能以正确的形式立即强制输出​​:(

    结论。除了 Sharpe 之外的所有内容都被正确推导。完全正确。简单地说,根据历史(显然是专利/自行车规则),他们使用不同的转换算法。夏普脱颖而出,但我认为这有历史原因(速度优化或人为错误)。

    • 20

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5