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!
Rounding involves converting a numeric value with a specified precision to a value with less precision. For example, you can use the Round (Double) method to round a value of 3.4 to 3.0, and the Round (Double, Int32) method to round a value of 3.579 to 3.58.
Round a number to a specified number of fractional digits by using a specified rounding convention. Round a Single value to a specified number of fractional digits by using a specified rounding convention and minimizing the loss of precision. Convert the Single to a Decimal and call Round (Decimal, Int32, MidpointRounding).
Rounding to nearest, or banker's rounding. Midpoint values are rounded to the nearest even number. For example, both 3.75 and 3.85 round to 3.8, and both -3.75 and -3.85 round to -3.8. This form of rounding is represented by the MidpointRounding.ToEven enumeration member.
Rounding involves converting a numeric value with a specified precision to the nearest value with less precision. For example, you can use the Round (Double) method to round a value of 3.4 to 3.0, and the Round (Double, Int32) method to round a value of 3.579 to 3.58.
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.Round
tries to take this into account to some extent, but I'm not sure of the exact details.
The solution - where possible - is to use decimal
instead of double
for 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:
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));
}
}
}
Results:
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 ...
(Note how although it starts going 0.994 at 8, it continues as far as 15 - whereas Math.Round
"corrects" itself at 10.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With