什么时候应该使用整数进行算术运算而不是 float/double

When should I use integer for arithmetic operations instead of float/double

一些 SO 用户对我说,我不应该将 float/double 用于学生成绩之类的东西,请参阅最后的评论:

"Because it is often easier and safer to do all arithmetic in integers, and then convert to a suitable display format at the last minute, than it is to attempt to do arithmetic in floating point formats. "

我试过他说的,但结果并不令人满意。

   int grade1 = 580;
   int grade2 = 210;
   var average = (grade1 + grade2) / 2;       
   string result = string.Format("{0:0.0}", average / 100);

结果是 "3,0"

    double grade3 = 5.80d;
    double grade4 = 2.10d;
    double average1 = (grade3 + grade4) / 2;
    double averageFinal = Math.Round(average1);
    string result1 = string.Format("{0:0.0}", averageFinal);

结果 1 是 "4,0"

我希望得到 4,0,因为 3,95 的结果应该是 4,0。之所以有效,是因为我使用 Math.Round ,它再次仅适用于双精度或小数。这不适用于整数。

那我哪里错了?

您需要"convert to a suitable display format at the last minute":

string result = string.Format("{0:0.0}", average / 100.0);

我认为您在给定的两个选项中选错了。了解埃里克,如果你清楚你在做浮点运算,如舍入、平均等,那么他就不会建议使用整数。不使用 double 的原因是因为它们不能总是精确地表示一个十进制值(你不能在 double 中精确地表示 1.1)。

如果您想使用浮点数学但仍然保持小数精度高达 28 位有效数字,请使用 decimal:

decimal grade1 = 580;
decimal grade2 = 210;
var average = (grade1 + grade2) / 2;     
average = Math.Round(average);  
string result = string.Format("{0:0.0}", average);

average是int,100是int。 当你做 average/100 时,你有一个整数除以一个整数,你会得到一个整数,但由于 3.95 不是整数,它被截断为 3.

如果你想得到一个浮点数或一个双精度数作为你的结果,一个双精度数或浮点数必须参与算术。

在这种情况下,您可以将结果转换为双精度 (double)average/100 或除以双精度 average/100.0

你不想在最后一秒之前用 floats/decimals 做太多算术运算的唯一原因与你不只是在一开始就在长物理方程中插入变量值的原因相同,你失去了精度。当您必须处理数字的浮点表示时,这是数值方法中一个非常重要的概念,例如机器 epsilon.

没那么容易。

如果成绩是整数格式,那么以整数存储是可以的。

但除非您特别需要整数数学,否则我会在执行任何数学运算之前将 do decimal 转换为从数学运算中删除舍入误差。

对于财务计算,建议使用十进制。其实后缀是m表示money.

decimal (C# Reference)

The decimal keyword indicates a 128-bit data type. Compared to floating-point types, the decimal type has more precision and a smaller range, which makes it appropriate for financial and monetary calculations. The approximate range and precision for the decimal type are shown in the following table.

Float and Double 是浮点类型。你得到的范围比十进制大,但精度较低。小数的等级范围足够大。

decimal grade3 = 5.80m;
decimal grade4 = 2.10m;
decimal average1 = (grade3 + grade4) / 2;

int grade1 = 580;
int grade2 = 210;
var average = (grade1 + grade2) / 2;
string result = string.Format("{0:0.0}", average / 100);  // 3.0
Debug.WriteLine(result);

decimal avg = ((decimal)grade1 + (decimal)grade2) / 200m;  // 3.95
Debug.WriteLine(avg);
Debug.WriteLine(string.Format("{0:0.0}", avg));  // 4.0

首先,你提到的具体问题是一个让我很头疼的问题。您几乎永远不想做整数运算的问题,然后 then 将其转换为浮点类型,因为计算将完全以整数形式完成。我希望 C# 编译器对此发出警告;我经常看到这个。

其次,选择整数或小数运算而不是双精度运算的原因是双精度只能非常准确地表示分母为 2 的幂的分数。当你在 double 中说 0.1 时,你不会得到 1/10,因为 1/10 不是分母是 2 的任何幂的分数。您得到 最接近 到 1/10 的分数 确实 在分母中有 2 的幂。

这通常是 "close enough",直到它不是。当您在接近硬截止点时出现微小错误时,这尤其令人讨厌。例如,您想说一个学生必须有 2.4 GPA 才能满足某些条件,而您所做的涉及分母为 2 的分数的计算恰好是 2.39999999999999999956...

现在,你不一定能通过十进制算术摆脱这些问题;十进制算术也有同样的限制:它只能表示分母为十次方的分数。您尝试表示 1/3,并且您将在每次计算中得到一个小的但非零的错误。

因此,标准建议是:如果您正在做任何计算,在计算涉及分子中为 10 的幂的分数(例如财务计算)时,您期望精确算术,请使用小数,或进行计算 完全 整数,适当缩放。如果您正在进行涉及 物理 数量的计算,其中没有固有的 "base" 计算,则使用 "double".

那么为什么要使用整数而不是小数,反之亦然?整数运算可以更小更快;小数需要更多时间和 space。但最终您不应该担心这些微小的性能差异:选择最准确地反映您所从事的数学领域的数据类型并使用它。