Ошибка System.Math.Round? - PullRequest
       6

Ошибка System.Math.Round?

7 голосов
/ 18 июня 2010

Я писал функцию округления числа до двух мест.И я обнаружил ошибку, когда пытался округлить определенные значения.Итак, я запустил код:

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 30 3132 32.99 33.99 34.99 35.99 36.99 37.99 38.99 39.99

числа не округляются до следующего значения.

Когда я запускаю тот же код до 1500: я получаю числа:

1013 * 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,991058.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 1086.99 1085.99 10.99 10.99 10,999 10,999 10,999 10,999 1095 1095 1095 1095 1095 10951099,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,991149,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,991199.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.991224,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,991274,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 1015 *, которые не округляется до следующего номера!Кто-нибудь имеет представление о том, почему это происходит для этих конкретных номеров!

Ответы [ 2 ]

10 голосов
/ 18 июня 2010

Проблема в том, что когда вы берете значение "число + 0,995", это не будет точно число + 0,995 по обычным причинам двоичной плавающей запятой, не представляющей точно десятичные значения.

Некоторым это будет немного позади, а иногда это будет немного хуже. Когда значение меньше и вы округлите результат до двух десятичных знаков, вы получите бит «0,99». Возможно, что Math.Round пытается до некоторой степени принять это во внимание, но я не уверен в точных подробностях.

Решение - где это возможно - использовать decimal вместо double для вещей, где десятичные цифры имеют значение.

См. Мои статьи о двоичной с плавающей запятой и десятичной с плавающей запятой в .NET для получения дополнительной информации.

Вот программа, использующая мой код 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 ...

(Обратите внимание, что хотя начинает с 0,994 в 8, оно продолжается до 15 - тогда как Math.Round "исправляет" себя в 10).

0 голосов
/ 08 февраля 2013

Это не вопрос правильного обращения. Попробуйте объяснить это:

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 и разработать собственное округление, чтобы решить эту проблему.

...