0

The problem:

Given an input float (value), round another float (anotherValue) so it has the same significant figures as the first float (value).


What I have tried so far:

private static void Test()
{
    var value = 0.12345f;
    // ?? Strategy suggested by this post: http://stackoverflow.com/questions/3683718/is-there-a-way-to-get-the-significant-figures-of-a-decimal
    var significantFigures = decimal.GetBits((decimal)value);

    var anotherValue = 3.987654321;
    // ERROR: Argument 2: cannot convert from int[] to int
    var resultValue = (float) SetSignificantFigures((double)anotherValue, significantFigures.Length);

    // Desired result: resultValue = 3.988
}

This is the definition of SetSignificantFigures:

// Function suggested by this post: http://stackoverflow.com/questions/374316/round-a-double-to-x-significant-figures
public static double SetSignificantFigures(double d, int digits)
{
    double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
    return scale * Math.Round(d / scale, digits);
}

Blocking point: since decimal.GetBits returns int[], I don't know how to proceed (or if it is the correct strategy).

Xavier Peña
  • 7,399
  • 9
  • 57
  • 99
  • What is the definition of *significant digits* ? Like [this](https://en.wikipedia.org/wiki/Significant_figures) ? The reason I am asking is that at first I thought you wanted the integer part of the number, but looks too easy for the efforts you are trying. But honestly so far from what I understood from the definition I brought - I still do not see a contradiction in saying that significant digits equals the integer part. Can someone give an example to show I am wrong ? – Veverke Apr 13 '16 at 06:55
  • @Veverke Sorry if it is not the correct description in English (I thought the other posts suggested a similar definition?). What I mean is "how many digits are there after the floating point". Maybe the better way for me to explain it is with the same examples as in the post: the significant digits of 0.123 would be 3. The significant digits of 3.987654321 would be 9. Maybe I'll replace "digits" by "figures", as it seems to be the standard definition in English. – Xavier Peña Apr 13 '16 at 06:59
  • 1
    Convert the 1st float to string, culture invariant, then cut everything before "." with a regex or string. Split and get the string's length. Round the new number to the lenght's digit – RaidenF Apr 13 '16 at 07:01
  • Thanks for clarifying. As for my remark above and an example of significant digits - following wikipedia's definition - it contains one, I just needed to go through. So you are not looking for what wikipedia defines as significant digits - rather you simply want the fractional part of the number. – Veverke Apr 13 '16 at 07:02
  • I may be wrong, but the way floating-point numbers are stored, is there a way to identify *significant digits* unless we apply some sort of rounding? – dotNET Apr 13 '16 at 07:07
  • @XavierPeña So the significant digits of 0.1230 would be 4? In that case, how do you tell that apart from 0.123? Or does the C# code representation not matter, and do you count the digits in `0.123f.ToString()` vs. `0.1230f.ToString()`? In that case, are you prepared to accept that calculations that should logically give `0.123f` may actually give something like `0.1299999f` or `0.12300001f` due to floating point inaccuracies, which would give a very much misleading number of significant digits? –  Apr 13 '16 at 07:09
  • @Veverke Thanks for pointing that out. Now I see what is wrong with my example. `// Desired result: resultValue = 3.987` sould be `// Desired result: resultValue = 3.988`. Sorry about that, I am going to correct it. – Xavier Peña Apr 13 '16 at 07:09
  • @hvd No need to differentiate, `0.1230` should be equivalent to `0.123`. Although if somehow the input is `0.1230f` (which in my case is not likely) I accept that it considers it as "4 figures after the floating point". `are you prepared to accept that calculations that should logically give 0.123f may actually give something like 0.1299999f`: no, this wouldn't fit my needs. In this case I would preffer the `string` strategy proposed by K. Gkinis and Nitin. – Xavier Peña Apr 13 '16 at 07:14

2 Answers2

1

If you just want the number of digits why don't you parse the string equivalent of the number

        var value = 12.123f;
        var str = value.ToString().Split('.');
        int decimalCount = 0;
        if (str.Count() == 2)
        {
            decimalCount = str[1].Length; // this will give you 3.
        }
Nitin
  • 18,344
  • 2
  • 36
  • 53
  • Ideally I would like the result to remain as close as possible to the "rounded" result, and not just "remove all remaining figures". With your example, `0.1239` set to only 3 figures would become `0.123` instead of `0.124`. But it is a shortcut I wouldn't have thought of, and I am using it if no other options come around. Thanks. – Xavier Peña Apr 13 '16 at 07:06
  • Now I see what is wrong with my example. `// Desired result: resultValue = 3.987` sould be `// Desired result: resultValue = 3.988`. Sorry about that, I am going to correct it. – Xavier Peña Apr 13 '16 at 07:08
  • In hindsight, I see that you are giving me an equivalent of how to get `significantFigures`. I can still use `SetSignificantFigures` if I want to round the result given those `significantFigures`. Marking it as answered. Thanks. – Xavier Peña Apr 13 '16 at 07:34
0

Replace this

var resultValue = SetSignificantDigits(anotherValue, significantDigits);

with this

var resultValue = SetSignificantDigits(anotherValue, significantDigits.Length);

Or try this

var significantDigits = value.ToString().Split('.')[1].Length; //perhaps replace '.' with ','

And in the SetSignificantDigits-Function replace this

return scale * Math.Round(d / scale, digits);

with this

return Math.Round(scale * (d / scale), digits);

Then it looks like this:

    var value = 0.12345f;
    var significantDigits = value.ToString().Split(',')[1].Length;
    var anotherValue = 3.987654321;
    var resultValue = SetSignificantDigits(anotherValue, significantDigits);

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

And the result is 3.98765

Wudge
  • 357
  • 1
  • 6
  • 14