C++ std::setprecision in C#

本文关键字:in setprecision C++ std | 更新日期: 2023-09-27 18:10:22

我通过移植一些遗留的c++代码来选择c#,并希望保持输出相同。以前是

之类的东西
output << std::setprecision(10) << (double) value;

我想现在应该是

output.Write("{0:F10}", value);

但这并没有奏效。特别是值> 1会得到更多的数字。一个常见的在线建议是先Math.Round,但如果总长度为< 10,则会添加零。

所以我写在一起:

    // std::setprecision is not exactly the same as ":F10", mirror original behavior
    static string setPrecision(double value) {
        string ret = value.ToString();
        // Don't just Substring(0, 11), we need to apply rounding,
        // and don't always do this, we don't want to append zeroes,
        // for 10 digits + period, with 0.. not counting for total
        if(ret.Length > digits + 1)
            ret = Math.Round(value, digits + (value < 1 ? 1 : 0) - ret.IndexOf('.')).ToString();
        return ret;
    }

式中digits为静态常数;我当然可以把它变成一个变量,但是对于这个项目来说,这样做没有什么意义。

不过,这似乎过于复杂了。有没有更优雅的方式来获得传统的行为?

按要求输入/输出

// C++
double test = 0; out << std::setprecision(10);
test = 0.123456780;   out << test << ''n';
test = 0.0123456781;  out << test << ''n';
test = 0.11234567819; out << test << ''n';
test = 1.00234567899; out << test << ''n';
// C#
double test = 0;            
test = 0.123456780;   output.WriteLine(setPrecision(test));
test = 0.0123456781;   output.WriteLine(setPrecision(test));
test = 0.11234567819; output.WriteLine(setPrecision(test));
test = 1.00234567899; output.WriteLine(setPrecision(test));

生产:

0.12345678
0.0123456781
0.1123456782
1.002345679

与此同时,我注意到所有标题0似乎都不计入总数,而只是第一个;

// C++
test = 0.012345678906;    out << test << ''n'; // 0.01234567891
test = 0.0012345678906;   out << test << ''n'; // 0.001234567891
test = 0.00012345678906;  out << test << ''n'; // 0.0001234567891
// C#
test = 0.012345678906;   output.WriteLine(setPrecision(test)); // 0.0123456789
test = 0.0012345678906;  output.WriteLine(setPrecision(test)); // 0.0012345679
test = 0.00012345678906; output.WriteLine(setPrecision(test)); // 0.0001234568

如果没有更直接的解决办法,我就不得不纠正了。

C++ std::setprecision in C#

听起来您只是想打印具有特定有效位数的数字。您可以简单地使用G格式字符串来指定要使用的位数。

output.Write("{0:G10}", value);

你指的是有效数字。这其实很容易计算:

    public static string FormatSignificantFigures(double number, int figures)
    {
        int e = 0;
        while (number >= 10.0)
        {
            e += 1;
            number /= 10;
        }
        while (number < 1.0)
        {
            e -= 1;
            number *= 10;
        }
        figures--;
        number = Math.Round(number, figures);
        figures += 0 - e;
        while (e > 0)
        {
            number *= 10;
            e -= 1;
        }
        while (e < 0)
        {
            number /= 10;
            e += 1;
        }
        if (figures < 0)
        {
            figures = 0;
        }
        return number.ToString($"f{figures}");
    }

基本上,前两个while循环将数字归一化为[1,10)之间的值。

然后,我们将数字四舍五入到有效数字的数量(减去1,记住我们已经在前面有一个有效数字1)。然后我们恢复它,最后一行是c# 6.0的字符串插值:

return number.ToString("f" + figures);

测试代码为:

    public static void _Main(string[] args)
    {
        double[] numbers = new double[] { 0.012345678906, 0.0012345678906, 0.00012345678906, 0.123456789012, 1.234567890124, 12.345678901234, 123.45678901234, 1234.5678901234, 12345.678901234 };
        foreach (double number in numbers)
        {
            Console.WriteLine($"{number}: {FormatSignificantFigures(number, 3)}");
        }
    }

结果:

0.012345678906: 0.0123
0.0012345678906: 0.00123
0.00012345678906: 0.000123
0.123456789012: 0.123
1.234567890124: 1.23
12.345678901234: 12.3
123.45678901234: 123
1234.5678901234: 1230
12345.678901234: 12300

注意:这是一个快速的回答,我将提取其中的一些到另一个函数,它暂时不返回字符串,但这应该让你开始。