为什么编译器将2.3视为double而不是十进制数?

15
为什么编译器认为2.3是double类型,导致以下代码无法编译:
decimal x;
x = 2.3; // Compilation error - can not convert double to decimal.
x = (decimal) 2.3 // O.k.
为什么编译器不会像这样思考:
他想要得到一个十进制数,他给了我一个可以是十进制数的值,所以这就是十进制数!

为什么这不会导致编译错误:

short x;
x = 23; // O.K.

谁说23不是int类型的?


1
编译器会决定它,因为语言规范规定了它。 - jason
1
@JonH,我搜索了一下,没有找到任何东西。 - gdoron
1
@Jason,问题仍然是,为什么? - gdoron
2
正如我在一个答案中所评论的那样,在一般情况下,从 intshort 没有隐式转换。但是,从类型为 int常量表达式 中确实存在一种转换,但这是不同的。 - Jon Skeet
1
有趣,我不知道那个。 - user505255
显示剩余2条评论
4个回答

23

这里有很多问题。让我们将它们细分为小问题。

为什么字面值2.3的类型是double而不是decimal?

历史原因。C#被设计为"C-like语法"系列语言的一员,因此其外观和基本习惯用语对使用C-like语言的程序员来说是熟悉的。在几乎所有这些语言中,浮点文字都被视为二进制而不是十进制浮点数,因为最初C就是这样做的。

如果我从头开始设计一种新语言,我可能会使模棱两可的文字非法;每个浮点文字都必须明确地指定为double、single或decimal,等等。

为什么通常隐式转换double和decimal之间是非法的?

因为这样做可能是一个错误,有两个方面。

首先,double和decimal具有不同的范围和不同数量的“表现误差”——即实际表示的数量与您希望表示的精确数学数量之间的差异。将double转换为decimal或反之亦然是一件危险的事情,你应该确定你正确地进行了转换;强制你拼出转换调用引起注意的事实是你可能会失去精度或数量。

其次,double和decimal的用途非常不同。Double通常用于科学计算,在这种情况下1.000000000001和0.99999999999之间的差异远小于实验误差。累积小的表现误差无关紧要。Decimal通常用于需要完全准确到一分钱的精确金融计算。意外混合两者似乎是危险的。

有时候你不得不这样做;例如,在计算按揭分期付款或复利利息增值时,使用double更容易。在这些情况下,我们再次让你拼出你正在从double转换为decimal,以便清楚地表明如果你没有正确处理精度或数量损失可能会发生的程序点。

为什么将double文本转换为十进制文本是不合法的?为什么不能假装它是一个十进制文本呢?
C#不是一种“为你隐藏错误”的语言,而是一种“告诉你存在哪些错误,以便你修复”的语言。如果你意思是说“2.3m”,但是忘记了“m”,那么编译器应该告诉你。
那么为什么将整数文本(或任何整数常量)转换为short、byte等是合法的呢?
因为整数常量可以在编译时检查是否处于正确的范围内。并且从范围内的整数到较小的整型的转换始终是精确的;它从不失去精度或大小,不像double/decimal转换。此外,整数常量算术始终在“checked”上下文中执行,除非您使用未经检查的块覆盖它,因此甚至没有溢出的危险。
并且整数/short算术不太可能跨越“域”边界,就像double/decimal算术那样。Double算术很可能是科学的,decimal算术很可能是金融的。但是整数和short算术并不明确与不同的业务领域相关联。
使其合法意味着您不必编写将常量强制转换为正确类型的丑陋不必要的代码。
因此,没有理由使其非法,并且有理由使其合法。

感谢您的详细解释! - vc 74

12

这里有一些事情需要注意:

  • 在您的第一个示例中,您试图隐式地将double字面量转换为float,这是行不通的。
  • 所谓的工作行是实际上试图执行从doubledecimal的显式转换(这是允许的,但通常不是一个好主意),然后尝试进行从decimalfloat的隐式转换(这是不被允许的)。如果x应该声明为decimal,那么唯一需要的转换是从doubledecimal - 通常也不是个好主意。
  • 整数字面量的有效转换是由C# 4规范的6.1.9节中指定的“隐式常量表达式转换”实现的:

    类型为 int 常量表达式可以转换为类型 sbyte byte short ushort uint ulong ,前提是常量表达式的值在目标类型的范围内。

    对于long也有类似的转换方式,但对于double则没有。

基本上,当您编写一个浮点常量时,最好使用后缀显式指定类型:

double d = 2.3d;
float f = 2.3f;
decimal m = 2.3m;

谢谢!那么为什么编译器不允许将const double隐式转换为decimal的常量表达式? - gdoron
@gdoron: 首先回答:因为这就是语言规范所说的。现在从语言设计的原因来看,除了其他原因外,几乎永远不应该在doubledecimal之间进行转换。你看,双精度浮点数“2.3”并不完全等于2.3,如果你有那段代码,你几乎肯定应该使用一个十进制文字。基本上,语言正在保护你免于做一些愚蠢的事情。 - Jon Skeet

3

2.3是双精度类型(double),这是语言规则。除非数字字面量有一个F后缀(float)或M后缀(decimal),否则任何带有小数点的数字字面量都是double类型:

x = 2.3F; // fine

编译器也给了我这个有用的提示:
字面值类型为double的值无法隐式转换为类型'float',请使用“F”后缀创建此类型的字面值。

1

由于浮点数在计算和值域方面总是有一些困难,因此它们在立即表示法中始终是最大可能的类型(在您的情况下为Double)。

非浮点数在下面有某种相同的处理方式,因此它们可以无任何问题地转换。如果您的值超过变量的值范围,则可能会导致错误(例如对于Byte来说是257)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接