简体   繁体   English

C#编译器不限制浮点文字的小数部分的位数

[英]C# compiler does not limit the number of digits of fractional part of a floating-point literal

This is just for academic purpose. 这仅用于学术目的。

I noticed that for integral literals, we can declare up to 18446744073709551615 which is 2^64-1 or ulong.MaxValue . 我注意到对于整数文字,我们可以声明最多18446744073709551615 ,即2^64-1ulong.MaxValue Defining greater than this value produces a compile-time error. 定义大于此值会产生编译时错误。

And for floating-point literals, we can declare them with integral part up to 999...999 ( 9 is repeated 308 times). 对于浮点文字,我们可以用999...9999重复308次)的整数部分声明它们。 Declaring the integral part with more digits again produces a compile-time error. 再次使用更多数字声明整数部分会产生编译时错误。 One thing that interests me is that the compiler seems to allow us specify the fractional part unlimited number of digits. 我感兴趣的一件事是编译器似乎允许我们指定小数部分无限数字的位数。 Practically, unlimited number of digits for the fractional part does not make sense. 实际上,小数部分的无限数字位数没有意义。

Questions: 问题:

  1. Is there a constant representing the max number of digits internally defined by C# compiler for the fractional part of a floating-point number? 是否有一个常量表示C#编译器内部定义的最大位数,用于浮点数的小数部分?

  2. If such a constant exists, why does not C# compiler throw compile-time error when users specify the fractional parts beyond its limit? 如果存在这样的常量,当用户指定超出其限制的小数部分时,为什么C#编译器不会抛出编译时错误?

Minimal Working Example 1 最小工作示例1

namespace FloatingPoint
{
    class Program
    {
        static void Main(string[] args)
        {
            const ulong @ulong = 18446744073709551615;
            const double @double = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;

        }
    }
}

Minimal Working Example 2 最小工作示例2

using System;

namespace FloatingPoint
{
    class Program
    {
        static void Main(string[] args)
        {

            const double x01 = 0.9;
            const double x02 = 0.99;
            const double x03 = 0.999;
            const double x04 = 0.9999;

            const double x05 = 0.99999;
            const double x06 = 0.999999;
            const double x07 = 0.9999999;
            const double x08 = 0.99999999;

            const double x09 = 0.999999999;
            const double x10 = 0.9999999999;
            const double x11 = 0.99999999999;
            const double x12 = 0.999999999999;

            const double x13 = 0.9999999999999;
            const double x14 = 0.99999999999999;
            const double x15 = 0.999999999999999;
            const double x16 = 0.9999999999999999;

            const double x17 = 0.99999999999999999;
            const double x18 = 0.999999999999999999;
            const double x19 = 0.9999999999999999999;
            const double x20 = 0.99999999999999999999;

            Console.WriteLine(x01);
            Console.WriteLine(x02);
            Console.WriteLine(x03);
            Console.WriteLine(x04);
            Console.WriteLine(x05);
            Console.WriteLine(x06);
            Console.WriteLine(x07);
            Console.WriteLine(x08);
            Console.WriteLine(x09);
            Console.WriteLine(x10);
            Console.WriteLine(x11);
            Console.WriteLine(x12);
            Console.WriteLine(x13);
            Console.WriteLine(x14);
            Console.WriteLine(x15);
            Console.WriteLine(x16);
            Console.WriteLine(x17);
            Console.WriteLine(x18);
            Console.WriteLine(x19);
            Console.WriteLine(x20);

        }
    }
}

/* output:

0.9
0.99
0.999
0.9999
0.99999
0.999999
0.9999999
0.99999999
0.999999999
0.9999999999
0.99999999999
0.999999999999
0.9999999999999
0.99999999999999
0.999999999999999
1
1
1
1
1
*/

IL: IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       302 (0x12e)
  .maxstack  1
  IL_0000:  nop
  IL_0001:  ldc.r8     0.90000000000000002
  IL_000a:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_000f:  nop
  IL_0010:  ldc.r8     0.98999999999999999
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_001e:  nop
  IL_001f:  ldc.r8     0.999
  IL_0028:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_002d:  nop
  IL_002e:  ldc.r8     0.99990000000000001
  IL_0037:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_003c:  nop
  IL_003d:  ldc.r8     0.99999000000000005
  IL_0046:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_004b:  nop
  IL_004c:  ldc.r8     0.99999899999999997
  IL_0055:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_005a:  nop
  IL_005b:  ldc.r8     0.99999990000000005
  IL_0064:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_0069:  nop
  IL_006a:  ldc.r8     0.99999998999999995
  IL_0073:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_0078:  nop
  IL_0079:  ldc.r8     0.99999999900000003
  IL_0082:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_0087:  nop
  IL_0088:  ldc.r8     0.99999999989999999
  IL_0091:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_0096:  nop
  IL_0097:  ldc.r8     0.99999999999
  IL_00a0:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00a5:  nop
  IL_00a6:  ldc.r8     0.99999999999900002
  IL_00af:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00b4:  nop
  IL_00b5:  ldc.r8     0.99999999999989997
  IL_00be:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00c3:  nop
  IL_00c4:  ldc.r8     0.99999999999999001
  IL_00cd:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00d2:  nop
  IL_00d3:  ldc.r8     0.999999999999999
  IL_00dc:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00e1:  nop
  IL_00e2:  ldc.r8     0.99999999999999989
  IL_00eb:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00f0:  nop
  IL_00f1:  ldc.r8     1.
  IL_00fa:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_00ff:  nop
  IL_0100:  ldc.r8     1.
  IL_0109:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_010e:  nop
  IL_010f:  ldc.r8     1.
  IL_0118:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_011d:  nop
  IL_011e:  ldc.r8     1.
  IL_0127:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_012c:  nop
  IL_012d:  ret
} // end of method Program::Main
  1. Yes, but they aren't decimal digits 是的,但它们不是十进制数字
  2. Specification of fractional parts beyond the ability to exactly represent them is easy, when the specification is decimal and the representation is binary. 当规范为十进制且表示为二进制时,超出精确表示它们的能力的小数部分的规范是容易的。 0.3 already requires approximation. 0.3已经需要近似值。

In most cases, a floating point number will be an approximation of the desired real value anyway (unless it happens to be one of the values that can be represented exactly). 在大多数情况下,浮点数无论如何都将是所需实际值的近似值(除非它恰好是可以精确表示的值之一)。 Furthermore, the approximation is well-defined: simply round to the nearest representable value. 此外,近似是明确定义的:简单地舍入到最接近的可表示值。 On the other hand, there is no useful way to round an integer (or the integer part of a real number) to the nearest representable value. 另一方面,没有有用的方法将整数(或实数的整数部分)舍入到最接近的可表示值。 What does it mean to round 2^100 to 2^64-1, for example? 例如,将2 ^ 100舍入到2 ^ 64-1是什么意思?

I'm not aware of any limit in the number of decimals permitted in a floating point literal though it should be relatively simple to test whether such a limit does indeed exist, though if it does, it likely depends more on compiler internals than anything specific to floating point values themselves anyway. 我不知道浮点文字中允许的小数位数有任何限制,虽然测试这样的限制是否确实存在应该相对简单,但如果确实存在,它可能更多地取决于编译器内部而不是特定的任何内容。无论如何,要浮点值。 However I think it's worth thinking about whether it makes sense to limit the number of decimals in a literal at all. 但是我认为值得思考是否有必要限制文字中的小数位数。 I think the key point here is the difference between numbers that cannot be represented because they are outside the supported range of the double data type (which are picked up by the compiler), and numbers that cannot be represented exactly within the data type. 我认为这里的关键点是无法表示的数字之间的差异,因为它们超出了双数据类型支持的范围 (由编译器选取),以及无法在数据类型中准确表示的数字。

There are indeed many decimal numbers that cannot be represented exactly as doubles (eg 0.1), and yet the compiler silently accepts them, converting them to the closest representable value, and it would be a significant inconvenience if it did not. 确实有许多十进制数字不能完全表示为双精度数(例如0.1),但编译器默默地接受它们,将它们转换为最接近的可表示值,如果不这样做,则会带来很大的不便。 Why therefore should a literal with an excess of decimals be treated any differently? 那么为什么一个超过小数的文字应该被区别对待呢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM