Double round number

If I have a double (234.004223), etc., I would like to round this to x significant digits in C #.

For now, I can find ways to round to x decimal places, but that just removes precision if the number has 0.

For example, 0.086 to one decimal place becomes 0.1, but I would like it to stay at 0.08.

+44
math c # rounding significant-digits
Dec 17 '08 at 11:52
source share
11 answers

In the framework there is no built-in function for rounding (or truncating, as in your example), to a number of significant digits. One way you can do this is to scale your number so that your first significant digit immediately after the decimal point is rounded (or truncated) and then reduced. The following code should do the trick:

static double RoundToSignificantDigits(this double d, int digits){ if(d == 0) return 0; double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1); return scale * Math.Round(d / scale, digits); } 

If, as in your example, you really want to truncate, then you want:

 static double TruncateToSignificantDigits(this double d, int digits){ if(d == 0) return 0; double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1 - digits); return scale * Math.Truncate(d / scale); } 
+61
Dec 17 '08 at 13:01
source share

I have been using the pDaddy sigfig function for several months and found an error. You cannot take a log of a negative number, so if d is negative, the result is NaN.

The following fixes the error:

 public static double SetSigFigs(double d, int digits) { if(d == 0) return 0; decimal scale = (decimal)Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1); return (double) (scale * Math.Round((decimal)d / scale, digits)); } 
+20
Dec 17 '09 at 22:56
source share

It seems to me that you do not want to round to ten decimal places - you want to round to x significant digits. Therefore, in your example, you want to round 0.086 to one significant digit, not a decimal place.

Now, using double and rounding to a few significant digits is problematic to start with, due to the preservation of the binary files. For example, you can round 0.12 to a value close to 0.1, but 0.1 is not exactly representable as a double. You really shouldn't use decimal? Alternatively, is this really for show? If this is for display, I suspect that you should actually convert double directly to a string with the appropriate number of significant digits.

If you can answer these questions, I can try to find the appropriate code. It’s awful how it sounds, converting to the number of significant digits as a string by converting the number to a “full” string, and then finding the first significant digit (and then accepting the appropriate rounding after that) may well be the best way.

+14
Dec 17 '08 at 12:27
source share

If this is intended to be displayed (as you point out in a comment on Jon Skeet's answer), you should use the Gn format specifier . Where n is the number of significant digits - exactly what you need.

Here is a usage example if you want 3 significant digits (printed output in the comment of each line):

  Console.WriteLine(1.2345e-10.ToString("G3"));//1.23E-10 Console.WriteLine(1.2345e-5.ToString("G3")); //1.23E-05 Console.WriteLine(1.2345e-4.ToString("G3")); //0.000123 Console.WriteLine(1.2345e-3.ToString("G3")); //0.00123 Console.WriteLine(1.2345e-2.ToString("G3")); //0.0123 Console.WriteLine(1.2345e-1.ToString("G3")); //0.123 Console.WriteLine(1.2345e2.ToString("G3")); //123 Console.WriteLine(1.2345e3.ToString("G3")); //1.23E+03 Console.WriteLine(1.2345e4.ToString("G3")); //1.23E+04 Console.WriteLine(1.2345e5.ToString("G3")); //1.23E+05 Console.WriteLine(1.2345e10.ToString("G3")); //1.23E+10 
+12
Nov 06
source share

I found two errors in the P Daddy and Eric methods. This solves, for example, the accuracy error presented by Andrew Hancox in this Q&A. There was also a problem with round directions. 1050 with two significant digits is not 1000.0, it is 1100.0. Rounding was fixed using MidpointRounding.AwayFromZero.

 static void Main(string[] args) { double x = RoundToSignificantDigits(1050, 2); // Old = 1000.0, New = 1100.0 double y = RoundToSignificantDigits(5084611353.0, 4); // Old = 5084999999.999999, New = 5085000000.0 double z = RoundToSignificantDigits(50.846, 4); // Old = 50.849999999999994, New = 50.85 } static double RoundToSignificantDigits(double d, int digits) { if (d == 0.0) { return 0.0; } else { double leftSideNumbers = Math.Floor(Math.Log10(Math.Abs(d))) + 1; double scale = Math.Pow(10, leftSideNumbers); double result = scale * Math.Round(d / scale, digits, MidpointRounding.AwayFromZero); // Clean possible precision error. if ((int)leftSideNumbers >= digits) { return Math.Round(result, 0, MidpointRounding.AwayFromZero); } else { return Math.Round(result, digits - (int)leftSideNumbers, MidpointRounding.AwayFromZero); } } } 
+4
Sep 07 '11 at 13:12
source share

As John Skeet says: Better deal with this in the text area. As a rule: for purposes, show, do not try to round / change your floating point values, it never works 100%. The display is a secondary concern, and you must handle any special formatting requirements, such as working with strings.

I implemented my solution below a few years ago and has proven itself very reliable. It has been thoroughly tested and works well. Approximately 5 times longer runtime than P Daddy / Eric.

Input + output examples below in code.

 using System; using System.Text; namespace KZ.SigDig { public static class SignificantDigits { public static string DecimalSeparator; static SignificantDigits() { System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture; DecimalSeparator = ci.NumberFormat.NumberDecimalSeparator; } /// <summary> /// Format a double to a given number of significant digits. /// </summary> /// <example> /// 0.086 -> "0.09" (digits = 1) /// 0.00030908 -> "0.00031" (digits = 2) /// 1239451.0 -> "1240000" (digits = 3) /// 5084611353.0 -> "5085000000" (digits = 4) /// 0.00000000000000000846113537656557 -> "0.00000000000000000846114" (digits = 6) /// 50.8437 -> "50.84" (digits = 4) /// 50.846 -> "50.85" (digits = 4) /// 990.0 -> "1000" (digits = 1) /// -5488.0 -> "-5000" (digits = 1) /// -990.0 -> "-1000" (digits = 1) /// 0.0000789 -> "0.000079" (digits = 2) /// </example> public static string Format(double number, int digits, bool showTrailingZeros = true, bool alwaysShowDecimalSeparator = false) { if (Double.IsNaN(number) || Double.IsInfinity(number)) { return number.ToString(); } string sSign = ""; string sBefore = "0"; // Before the decimal separator string sAfter = ""; // After the decimal separator if (number != 0d) { if (digits < 1) { throw new ArgumentException("The digits parameter must be greater than zero."); } if (number < 0d) { sSign = "-"; number = Math.Abs(number); } // Use scientific formatting as an intermediate step string sFormatString = "{0:" + new String('#', digits) + "E0}"; string sScientific = String.Format(sFormatString, number); string sSignificand = sScientific.Substring(0, digits); int exponent = Int32.Parse(sScientific.Substring(digits + 1)); // (the significand now already contains the requested number of digits with no decimal separator in it) StringBuilder sFractionalBreakup = new StringBuilder(sSignificand); if (!showTrailingZeros) { while (sFractionalBreakup[sFractionalBreakup.Length - 1] == '0') { sFractionalBreakup.Length--; exponent++; } } // Place decimal separator (insert zeros if necessary) int separatorPosition = 0; if ((sFractionalBreakup.Length + exponent) < 1) { sFractionalBreakup.Insert(0, "0", 1 - sFractionalBreakup.Length - exponent); separatorPosition = 1; } else if (exponent > 0) { sFractionalBreakup.Append('0', exponent); separatorPosition = sFractionalBreakup.Length; } else { separatorPosition = sFractionalBreakup.Length + exponent; } sBefore = sFractionalBreakup.ToString(); if (separatorPosition < sBefore.Length) { sAfter = sBefore.Substring(separatorPosition); sBefore = sBefore.Remove(separatorPosition); } } string sReturnValue = sSign + sBefore; if (sAfter == "") { if (alwaysShowDecimalSeparator) { sReturnValue += DecimalSeparator + "0"; } } else { sReturnValue += DecimalSeparator + sAfter; } return sReturnValue; } } } 
+3
Aug 20 '14 at 9:48
source share

Math.Round () error in doubles (see notes for callers in the documentation ). A later step in multiplying the rounded number by its decimal exponent will lead to additional floating point errors in the final digits. Using another Round () as @Rowanto will not help reliably and suffers from other problems. However, if you are ready to go through the decimal code, then Math.Round () is reliable, since it is multiplied and divides by degrees 10:

 static ClassName() { powersOf10 = new decimal[28 + 1 + 28]; powersOf10[28] = 1; decimal pup = 1, pdown = 1; for (int i = 1; i < 29; i++) { pup *= 10; powersOf10[i + 28] = pup; pdown /= 10; powersOf10[28 - i] = pdown; } } /// <summary>Powers of 10 indexed by power+28. These are all the powers /// of 10 that can be represented using decimal.</summary> static decimal[] powersOf10; static double RoundToSignificantDigits(double v, int digits) { if (v == 0.0 || Double.IsNaN(v) || Double.IsInfinity(v)) { return v; } else { int decimal_exponent = (int)Math.Floor(Math.Log10(Math.Abs(v))) + 1; if (decimal_exponent < -28 + digits || decimal_exponent > 28 - digits) { // Decimals won't help outside their range of representation. // Insert flawed Double solutions here if you like. return v; } else { decimal d = (decimal)v; decimal scale = powersOf10[decimal_exponent + 28]; return (double)(scale * Math.Round(d / scale, digits, MidpointRounding.AwayFromZero)); } } } 
+2
Dec 14 '12 at 3:00
source share

This question is similar to the one you are asking:

Formatting numbers with significant digits in C #

So you can do the following:

 double Input2 = 234.004223; string Result2 = Math.Floor(Input2) + Convert.ToDouble(String.Format("{0:G1}", Input2 - Math.Floor(Input2))).ToString("R6"); 

Rounded to 1 significant digit.

+1
Dec 17 '08 at 12:09
source share

Enter the inputNumber you want to convert using significantDigitsRequired after the decimal point, then significantDigitsResult is the answer to the following pseudo-code.

 integerPortion = Math.truncate(**inputNumber**) decimalPortion = myNumber-IntegerPortion if( decimalPortion <> 0 ) { significantDigitsStartFrom = Math.Ceil(-log10(decimalPortion)) scaleRequiredForTruncation= Math.Pow(10,significantDigitsStartFrom-1+**significantDigitsRequired**) **siginficantDigitsResult** = integerPortion + ( Math.Truncate (decimalPortion*scaleRequiredForTruncation))/scaleRequiredForTruncation } else { **siginficantDigitsResult** = integerPortion } 
+1
Dec 18 '08 at 5:02
source share

Here is what I did in C ++

 /* I had this same problem I was writing a design sheet and the standard values were rounded. So not to give my values an advantage in a later comparison I need the number rounded, so I wrote this bit of code. It will round any double to a given number of significant figures. But I have a limited range written into the subroutine. This is to save time as my numbers were not very large or very small. But you can easily change that to the full double range, but it will take more time. Ross Mckinstray rmckinstray01@gmail.com */ #include <iostream> #include <fstream> #include <string> #include <math.h> #include <cmath> #include <iomanip> #using namespace std; double round_off(double input, int places) { double roundA; double range = pow(10, 10); // This limits the range of the rounder to 10/10^10 - 10*10^10 if you want more change range; for (double j = 10/range; j< 10*range;) { if (input >= j && input < j*10){ double figures = pow(10, places)/10; roundA = roundf(input/(j/figures))*(j/figures); } j = j*10; } cout << "\n in sub after loop"; if (input <= 10/(10*10) && input >= 10*10) { roundA = input; cout << "\nDID NOT ROUND change range"; } return roundA; } int main() { double number, sig_fig; do { cout << "\nEnter number "; cin >> number; cout << "\nEnter sig_fig "; cin >> sig_fig; double output = round_off(number, sig_fig); cout << setprecision(10); cout << "\n I= " << number; cout << "\nr= " <<output; cout << "\nEnter 0 as number to exit loop"; } while (number != 0); return 0; } 

Hope I haven’t changed anything by formatting it.

-3
Mar 17 2018-12-17T00:
source share

I just did:

 int integer1 = Math.Round(double you want to round, significant figures you want to round to) 
-5
May 2 '11 at 19:22
source share



All Articles