System.Math.Round错误?

时间:2010-06-18 06:05:09

标签: .net rounding

我正在编写一个将数字四舍五入到两个地方的函数。当我试图围绕特定值时,我发现了一个错误。所以,我运行了代码:

class Program {
    static void Main(string[] args) {
        int limit = 100;

        for (int number = 0; number <= limit; number++) {
            Console.WriteLine((System.Math.Round((double)(number+0.995),2,MidpointRounding.AwayFromZero)));
        }
    }
}

我发现: 的 8.99 9.99 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 三十 31 32 的 32.99 33.99 34.99 35.99 36.99 37.99 38.99 39.99

数字未四舍五入到下一个值。

当我运行相同的代码直到1500:我得到数字:

8.99 9.99 32.99 33.99 34.99 35.99 36.99 37.99 38.99 39.99 1024.99 1025.99 1026.99 1027.99 1028.99 1029.99 1030.99 1031.99 1032.99 1033.99 1034.99 1035.99 1036.99 1037.99 1038.99 1039.99 1040.99 1041.99 1042.99 1043.99 1044.99 1045.99 1046.99 1047.99 1048.99 1049.99 1050.99 1051.99 1052.99 1053.99 1054.99 1055.99 1056.99 1057.99 1058.99 1059.99 1060.99 1061.99 1062.99 1063.99 1064.99 1065.99 1066.99 1067.99 1068.99 1069.99 1070.99 1071.99 1072.99 1073.99 1074.99 1075.99 1076.99 1077.99 1078.99 1079.99 1080.99 1081.99 1082.99 1083.99 1084.99 1085.99 1086.99 1087.99 1088.99 1089.99 1090.99 1091.99 1092.99 1093.99 1094.99 1095.99 1096.99 1097.99 1098.99 1099.99 1100.99 1101.99 1102.99 1103.99 1104.99 1105.99 1106.99 1107.99 1108.99 1109.99 1110.99 1111.99 1112.99 1113.99 1114.99 1115.99 1116.99 1117.99 1118.99 1119.99 1120.99 1121.99 1122.99 1123.99 1124.99 1125.99 1126.99 1127.99 1128.99 1129.99 1130.99 1131.99 1132.99 1133.99 1134.99 1135.99 1136.99 1137.99 1138.99 1139.99 1140.99 1141.99 1142.99 1143.99 1144.99 1145.99 1146.99 1147.99 1148.99 1149.99 1150.99 1151.99 1152.99 1153.99 1154.99 1155.99 1156.99 1157.99 1158.99 1159.99 1160.99 1161.99 1162.99 1163.99 1164.99 1165.99 1166.99 1167.99 1168.99 1169.99 1170.99 1171.99 1172.99 1173.99 1174.99 1175.99 1176.99 1177.99 1178.99 1179.99 1180.99 1181.99 1182.99 1183.99 1184.99 1185.99 1186.99 1187.99 1188.99 1189.99 1190.99 1191.99 1192.99 1193.99 1194.99 1195.99 1196.99 1197.99 1198.99 1199.99 1200.99 1201.99 1202.99 1203.99 1204.99 1205.99 1206.99 1207.99 1208.99 1209.99 1210.99 1211.99 1212.99 1213.99 1214.99 1215.99 1216.99 1217.99 1218.99 1219.99 1220.99 1221.99 1222.99 1223.99 1224.99 1225.99 1226.99 1227.99 1228.99 1229.99 1230.99 1231.99 1232.99 1233.99 1234.99 1235.99 1236.99 1237.99 1238.99 1239.99 1240.99 1241.99 1242.99 1243.99 1244.99 1245.99 1246.99 1247.99 1248.99 1249.99 1250.99 1251.99 1252.99 1253.99 1254.99 1255.99 1256.99 1257.99 1258.99 1259.99 1260.99 1261.99 1262.99 1263.99 1264.99 1265.99 1266.99 1267.99 1268.99 1269.99 1270.99 1271.99 1272.99 1273.99 1274.99 1275.99 1276.99 1277.99 1278.99 1279.99 1280.99 1281.99 1282.99 1283.99 1284.99 1285.99 1286.99 1287.99 1288.99 1289.99 1290.99 1291.99 1292.99 1293.99 1294.99 1295.99 1296.99 1297.99 1298.99 1299.99 1300.99 1301.99 1302.99 1303.99 1304.99 1305.99 1306.99 1307.99 1308.99 1309.99

未舍入到下一个数字!有没有人知道为什么它会发生这些特定的数字!

2 个答案:

答案 0 :(得分:10)

问题在于,当你取“数字+ 0.995”的值时,由于二进制浮点的正常原因不能精确地表示十进制值,因此不会正好数字+ 0.995

有些受害者会有点过头,有时它会有点过分。如果下,并将结果舍入到两位小数,则最终得到“0.99”位。 Math.Round可能会在某种程度上考虑到这一点,但我不确定完全的详细信息。

解决方案 - 如果可能 - 使用decimal代替double来处理十进制数字的事情。

有关详细信息,请参阅我在.NET上的binary floating pointdecimal浮动点上的文章。

这是一个程序,使用我的DoubleConverter代码,显示添加0.995后的不准确性:

using System;

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 100; i++)
        {
            double d = i + 0.995;
            Console.WriteLine(DoubleConverter.ToExactString(d));
        }
    }
}

结果:

  

0.99499999999999999555910790149937383830547332763671875   1.99500000000000010658141036401502788066864013671875   2.99500000000000010658141036401502788066864013671875   3.99500000000000010658141036401502788066864013671875   4.99500000000000010658141036401502788066864013671875   5.99500000000000010658141036401502788066864013671875   6.99500000000000010658141036401502788066864013671875   7.99500000000000010658141036401502788066864013671875   8.9949999999999992184029906638897955417633056640625   9.9949999999999992184029906638897955417633056640625   10.9949999999999992184029906638897955417633056640625   11.9949999999999992184029906638897955417633056640625   12.9949999999999992184029906638897955417633056640625   13.9949999999999992184029906638897955417633056640625   14.9949999999999992184029906638897955417633056640625   15.9949999999999992184029906638897955417633056640625   16.995000000000000994759830064140260219573974609375   17.995000000000000994759830064140260219573974609375   18.995000000000000994759830064140260219573974609375   19.995000000000000994759830064140260219573974609375   20.995000000000000994759830064140260219573974609375   ...

(注意虽然开始在8处开始为0.994,但它仍然持续到15 - 而Math.Round“在10处自我纠正。”

答案 1 :(得分:0)

这不是正确处理的问题。试着解释一下:

Console.WriteLine(Math.Round(4.995, 2, MidpointRounding.AwayFromZero)); // 5.00
Console.WriteLine(Math.Round(5.995, 2, MidpointRounding.AwayFromZero)); // 6.00
Console.WriteLine(Math.Round(9.995, 2, MidpointRounding.AwayFromZero)); // 9.99

您可以很容易地看到,1%远远大于双精度限制。并且没有任何算术运算可以归咎于精确损失。

Math.Round和double.ToString()具有不同的舍入行为。 Math.Round有时不起作用。我不得不放弃Math.Round并且必须开发自己的舍入来解决这个问题。