首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C和C#之间浮点精度行为的差异

C和C#之间浮点精度行为的差异
EN

Stack Overflow用户
提问于 2020-09-01 15:02:34
回答 1查看 151关注 0票数 2

这是一个学术问题,所以像“不要那样做”这样的回答没有切中要害。

我并不是在尝试解决问题--我是在尝试理解观察到的行为,即在比较C和C#时浮点数学的运行方式的不同

假设:C语言中的浮点精度

我假设在C语言中,floats是使用23位尾数和8位指数(https://en.wikipedia.org/wiki/Single-precision_floating-point_format)实现的。

对于给定的数字,我们可以通过计算尾数的最后一位的值来计算最小精度-您可以将其添加到纯结构上不能再存储的数字的最小值。

如果浮点数的计算结果为:

代码语言:javascript
复制
[sign] * 1.[mantissa] * 2^[exponent]

然后,因为尾数中有23位,所以精度的值是2^(exponent-23),其中给定数字的指数是:

代码语言:javascript
复制
floor(log2(number))

因此,像10^9这样一个相当大的数字的精度计算如下:

代码语言:javascript
复制
exponent  = floor(log2(10^9))
          = 29

precision = 2^(exponent-23)
          = 2^(29-23)
          = 2^6
          = 64

这是理论上最低的裸机值,当存储为浮点数时,可以添加到10^9中,因为我们从字面上翻转了尾数的最低有效位:

As visualized by the IEEE-754 Floating Point Converter

我还可以用一个快速C程序(run online)来验证这一点:

代码语言:javascript
复制
#include <cstdio>

int main()
{  
  float number = 1e9f;          // exponent: 29, precision: 64
  printf("%'.0f\n", number);    // prints: 1000000000 
  
  number += 30;                 // 30 rounded to nearest multiple of 64 is 0 
  printf("%'.0f\n", number);    // prints: 1000000000 
  
  number += 40;                 // 40 rounded to nearest multiple of 64 is 64
  printf("%0'.0f\n", number);   // prints: 1000000064 
  
  return 0;
}

我假设通用的32位浮点格式(1位符号,8位指数,23位尾数)是如此通用,以至于它是现代CPU固有的东西,因此通常在不同编程语言中的行为是相同的。

问题:C语言中的浮点精度

如上所述,当我在C#中尝试相同的验证测试时,数字的值不会改变。

如果我使用一个较小的值10^8,它的指数为26,因此精度为2^(26-23) = 8,根据上面关于浮点格式的位如何在内部表示数字的假设,我注意到以下行为:

代码语言:javascript
复制
float number = 1e8f;                 // exponent: 26, precision: 8
Console.WriteLine($"{number,1:0}");  // prints: 100000000 

number += 30;                        // 30 rounded to multiple of 8 -should- be 32
Console.WriteLine($"{number,1:0}");  // prints: 100000000 

number += 40;                        // 40 rounded to multiple of 8 -should- be 40
Console.WriteLine($"{number,1:0}");  // prints: 100000100

而那..。让我有点迷惑。这100块是从哪里来的?这甚至不是2的倍数!

值为1e8f的C的行为也与预期相同,并支持精度为值'8':cpp.sh/6qesv

看看C# documentation for floating point values,我没有想到C#处理浮点加法应该与C语言有所不同,以及我期望的浮点值是如何实现的。

文档确实提到了浮点数的近似精度是~6-9位,这是令人沮丧的模糊。我认为这可能是一个答案:“你正在处理超过保证限制的数字,这是未定义的行为”,虽然这是真的,但这是不令人满意的。

我想知道,最好是一步一步地分解,在C#的实现中到底发生了什么,使得它的行为与这里的C如此不同。

EN

回答 1

Stack Overflow用户

发布于 2020-09-02 03:02:59

将我的评论提升为答案:

这里的问题不是浮点,而是字符串格式的差异。我不太熟悉指定"0“的格式的确切含义或作用(似乎在任何地方都找不到它的文档),但它是造成您看到的不寻常的舍入的原因。

使用格式说明符"G9“是recommended,用于格式化单精度浮点数,使其能够正确地往返(这意味着将字符串解析回单精度浮点数将精确地重现原始值)。如果您将代码更改为在插值字符串中使用{number:G9},您应该会看到预期的结果。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63682864

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档