I was writing a function for rounding a number to two places. And I found a bug when I was trying to round specific values. So, I ran the code:
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)));
}
}
}
And I found that:
8.99
9.99
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
32.99
33.99
34.99
35.99
36.99
37.99
38.99
39.99
numbers are not rounded to their next value.
When I run the same code till 1500: I get the numbers:
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
which are not rounded to next number! Has anyone any idea about why its happening for these specific numbers!
The problem is that when you’re taking the value of “number + 0.995”, that’s not going to be exactly number + 0.995 for the normal reasons of binary floating point not representing decimal values precisely.
Sometims it will be a little over, and sometimes it’ll be a little under. When it’s under, and you round that result to two decimal places, you end up with the “0.99” bit. It’s possible that
Math.Roundtries to take this into account to some extent, but I’m not sure of the exact details.The solution – where possible – is to use
decimalinstead ofdoublefor things where the decimal digits matter.See my articles on binary floating point and decimal floating point in .NET for more information.
Here’s a program using my DoubleConverter code which shows the inaccuracy after adding 0.995:
Results:
(Note how although it starts going 0.994 at 8, it continues as far as 15 – whereas
Math.Round“corrects” itself at 10.)