这只是为了学术目的。
我注意到对于整数字面量,我们可以声明最多18446744073709551615
,即2^64-1
或ulong.MaxValue
。定义大于此值会产生编译时错误。
对于浮点字面量,我们可以将它们的整数部分声明为999...999
(9
重复308次)。再次声明具有更多数字的整数部分会产生编译时错误。有趣的一件事是,编译器似乎允许我们指定无限数量的小数部分。实际上,小数部分的无限位数没有意义。
问题:
是否存在一个常量来表示C#编译器内部定义的浮点数小数部分的最大位数?
如果存在这样的常量,为什么C#编译器不会在用户指定超出其限制的小数部分时抛出编译时错误?
最小工作示例1
namespace FloatingPoint
{
class Program
{
static void Main(string[] args)
{
const ulong @ulong = 18446744073709551615;
const double @double = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
}
}
}
最小工作示例 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:
.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
float
或double
的实数文字量的值是通过使用IEEE“四舍五入到最近”模式来确定的。" 因此,在这种情况下,那个超长的值可以被“表示”为浮点数(因为几乎所有数字都是估算),或者它可能使用了“IEEE四舍五入到最近模式”。 - Chris Sinclair0.9999999999999995
被表示为0.999999999999999
,而0.99999999999999951
则被表示为1
。 - Preston GuillotDouble.MinValue
和Double.MaxValue
之间)。因此,const double d = 0.999...
(重复2000个数字)在IL代码中被编译为1
。也就是说,const double @d = 1;
编译成与const double @double = 0.999...;
完全相同的IL代码。这可能看起来不合理,但由于任何你输入的分数都不太可能存在一个精确值,所以它可能使用相同的近似规则并且是可表示的,而在Min/Max范围之外则是不可表示的(因此会出现错误)。 - Chris Sinclair