为什么具有许多有效数字的数字在 C# 和 JavaScript 中的处理方式不同?

Why are numbers with many significant digits handled differently in C# and JavaScript?

如果 JavaScript 的 Number 和 C# 的 double 指定相同 (IEEE 754),为什么具有许多有效数字的数字处理不同?

var x = (long)1234123412341234123.0; // 1234123412341234176   - C#
var x =       1234123412341234123.0; // 1234123412341234200   - JavaScript

我不关心 IEEE 754 不能表示数字 1234123412341234123 的事实。我关心的是这两种实现对于不能完全精确表示的数字的行为不同。

这可能是因为 IEEE 754 未指定,一个或两个实现有问题,或者它们实现了 IEEE 754 的不同变体。

此问题与 C# 中的浮点输出格式化问题无关。我正在输出 64 位整数。考虑以下因素:

long x = 1234123412341234123;
Console.WriteLine(x); // Prints 1234123412341234123
double y = 1234123412341234123;
x = Convert.ToInt64(y);
Console.WriteLine(x); // Prints 1234123412341234176

同一个变量打印不同的字符串,因为值不同。

这里有多个问题...

您正在使用 long 而不是 double。你需要写:

double x = 1234123412341234123.0;

var x = 1234123412341234123.0;

另一个问题是 .NET 在将其转换为 string 之前将 doubles 舍入为 15 位数字(因此在使用 Console.ToString() 打印之前)。

例如:

string str = x.ToString("f"); // 1234123412341230000.00

参见示例

内部号码仍然是17位,只是显示为15。

如果你做一个就可以看到它:

string str2 = x.ToString("r"); // 1.2341234123412342E+18

数字没有不同处理,只是显示不同。

.NET 显示 15 位有效数字,而 JavaScript 显示 17 位有效数字。 double 的表示可以包含 15-17 位有效数字,具体取决于它包含的数字。 .NET 只显示数字保证始终支持的位数,而 JavaScript 显示所有数字但可能会出现精度限制。

.NET 在指数为 15 时开始使用科学记数法,而 JavaScript 在指数为 21 时开始使用科学计数法。这意味着 JavaScript 将显示 18 到 20 位数字填充的数字末尾为零。

在您的示例中将 double 转换为 long 将绕过 .NET 显示双倍的方式。该数字将在不进行隐藏精度限制的舍入的情况下进行转换,因此在这种情况下,您会看到 double 中的实际值。第 17 位之后不只有零的原因是该值以二进制形式存储,而不是十进制形式。

免责声明:这是基于标准 wikipedia page

根据标准的维基百科页面,它定义了 decimal64 应该有 16 位精度。

过去由实施者决定如何处理额外的数字。

所以您可能会说标准未指定,但这些数字无论如何都不是为符合标准规范而设计的。两种语言都有处理更大数字的方法,因此这些选项可能更适合您。