十进制和数学运算

19

我有一个在 C# 中将 decimal(十进制数)进行简单转换的方法。代码如下:

private decimal BaseValue
{
    get; set;
}

public decimal ConvertedValue 
{
    get
    {
        return BaseValue * (365 / 360);
    }
}

但是,这个方法不起作用。我认为是因为C#将分数中的数字视为整数处理。所以我可以像这样做(它可以实现相同的效果):

public decimal ConvertedValue 
{
    get
    {
        return BaseValue * (decimal)((double)365 / (double)360);
    }
}

现在似乎有点过度,但我可以接受。我的主要问题是:

为什么Visual Studio会警告我'Cast is redundant',对于(double)的转换?如果我删除(double)的转换,那么(decimal)的转换就变得多余了。如果我删除它,那么我回到了不起作用的解决方案。帮忙……?


2
为什么要使用大括号?如果您不了解优先级规则隐式转换逻辑,似乎这是一种强有力的备份策略,但有时会导致问题... - Sinatr
@Sinatr,OP 代码中的任何括号都不能更改,否则将改变代码的语法树。 OP 的问题不在于他们不理解优先级:他们已经正确地确定了第一个代码片段为什么不起作用。 - wchargin
@Sinatr 我知道运算优先级和“PEMDAS”。括号部分是为了让这两个例子保持相似,但我也使用它们来提高其他开发人员的可读性。 - Jakob Busk Sørensen
10个回答

26

解决这个问题的一种方法是指定计算中的数字为带有结尾 mdecimal 类型。

return BaseValue * (365m / 360m);

为什么在使用(double)进行转换时,Visual Studio会提醒我'Cast is redundant'?

因为当等式中一侧已经是double类型时,结果将自动转换为double,所以这个转换被认为是多余的。

(double)365 / (double)360

请参阅* 运算符重载的文档,您会发现操作数始终是相同类型的,例如:

decimal operator *(decimal x, decimal y);


……那么(decimal)转换就变得多余了。

同样地,因为当等式中的一侧为decimal时,结果将为decimal:

BaseValue * (decimal)(365 / 360)

问题在于范围!您将整个除法结果转换为decimal。实际上,您可以通过删除括号来解决问题:

return BaseValue * 365 / 360;

这样做可以使你的等式正确,因为 * 乘法的结果将是一个 decimal(因为其中一个操作数是 decimal,所以另一个将被隐式转换),同样的原因,除法的结果也将是十进制。

注意:通常情况下,去掉括号和保留括号不是一样的。在某些情况下,浮点运算的结果会因为操作顺序的变化而不同,即使两个表达式在数学上是相同的。评论来自Banex

编辑:

这个 m 东西被称为 字面量。有关所有类型后缀或字面量的更多信息,请参见此处的文档


1
感谢详细的解释。最终我使用了数字后面的 m,这使得它起作用了。m 是否有任何含义,还是只是一个任意的符号,被选择来表示小数? - Jakob Busk Sørensen
1
@Noceo 我猜 m 代表金钱,因为这是在计算货币值时建议使用的类型。但最好相信 Jon Skeet 在这篇文章中的描述 ;) - Mong Zhu
这可能是有道理的。你永远不知道,如果这只是某个诵读障碍的开发者的工作 :-) - Jakob Busk Sørensen
@Noceo :D 可能确实是这种情况。但我认为更有可能的是 d 已经被赋值为 double,所以他们选择了下一个可用的字母 ;) - Mong Zhu
1
删除括号通常不等于保留括号。在某些情况下,当这种操作的顺序改变时,浮点运算的结果会有所不同,即使这两个表达式在数学上是相同的。 - Banex
@Banex 已授权。我会将其纳入我的答案中。 - Mong Zhu

8
decimal类型转换是多余的,因为编译器知道你想返回一个decimal类型。
两个double类型转换中有一个是多余的,因为当你将其中一个int类型转换为double类型时,很明显你使用的是double类型的除法运算符而不是整数除法运算符。
但只需使用m作为decimal字面量后缀即可。
return BaseValue * (365m / 360);

再次强调,一个m足以推断正确的运算符。


但是嘿,如果你不想进行整数除法计算,BaseValue已经是decimal类型了,括号没有意义……这种代码也能正常工作:

return BaseValue * 365 / 360;

@Fildor,改变计算顺序肯定会有精度差异,但我猜它们也取决于“BaseValue”的值。 - René Vogt

4

您可以只在其中一个数字后添加m后缀,使其变为十进制:

return BaseValue * (365 / 360m);

4

使用m后缀:

return 365m/360 * BaseValue;

4

有一些数字类型的后缀,例如:

 // Use long suffix.
 long l1 = 10000L;

 // Use double suffix.
 double d1 = 123.764D;

 // Use float suffix.
 float f1 = 100.50F;

 // Use unsigned suffix.
 uint u1 = 1000U;

 // Use decimal suffix.
 decimal m2 = 4000.1234M;

 // Use unsigned suffix and long suffix.
 ulong u2 = 10002000300040005000UL;

后缀指定数字类型,它们告诉C#编译器将整数字面量(如1000)视为某种类型的数字,例如long(1000L)。我们将探讨如何将数字后缀添加到数字中。
在你的情况下:
public decimal ConvertedValue 
{
    get
    {
        return BaseValue * (365 / 360M);
    }
}

使用大写后缀时更加清晰明了:

小写后缀。您还可以指定小写后缀,例如u、l、ul、f、d和m。但这些更容易与数字混淆。字母“l”有时被视为数字1。


2
你只需要使用其中的一个双重转换。因此,
return BaseValue * (decimal)(365/(double)360);

将会很好地工作。

一旦一个数是双精度,编译器就知道要将其视为非整数除法。

或者

return (BaseValue*365)/360;

将会起作用。

甚至更多。

return BaseValue*365/360;

由于乘法的优先级高于除法。


2
您可以使用十进制字面量,通过使用“m”后缀来简单实现:
public decimal ConvertedValue 
{
    get
    {
        return BaseValue * (365<b>m</b>/360);
    }
}

第二次转换是多余的原因是,C# 推断你在 ((double)365 / (double)360) 表达式中的第一个数字(365)是一个 double。因此为了计算除法,它将隐式地将第二个元素转换为 double。因此,无论你是否写了 (double) 作为第二个元素都无所谓。
csharp> (double) 365/350        
1.04285714285714                
csharp> (double) 365/ (double) 350
1.04285714285714

然而,将数值转换为双精度浮点数再转回十进制数是没有任何作用的。通过在表达式中使用一个十进制数字面量,另一个数字也将成为十进制数,因此我们仍然处于decimal世界中。


如果您在此处使用 m,则无需使用 .0 后缀。 - DavidG

2
在你的例子中,你可以省略括号:
return BaseValue*365/360;

0
一个双精度除以一个双精度仍然是双精度。因此,强制转换结果是多余的。
如果您直接将数字指定为十进制数,则会更短:
 return BaseValue * (365m /360);

0

两个(double)转换中的任何一个都是多余的,但不能同时省略。 如果操作的任何一个参数是double类型,则另一个参数会自动转换为double类型。 你能否将其中一个参数写成实数常量,例如365.0?


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