如何在c++中对包含双值的字符串强制执行精度

how do i enforce precision on a string containing a double value in c++

本文关键字:字符串 强制执行 精度 包含双 c++      更新时间:2023-10-16

示例输入字符串:

char *str = "12345.567675";

以及所需的输出,如果我需要小数点后3位的精度:

str = "12345.568";

有没有一种方法可以做到这一点,而不将字符串转换为double并返回字符串?

是的,在高级:
1.搜索"。"在字符串中
2.如果'.'+的位置3比你做的长度小
3.否则,将字符串插入'.'+的位置3.
4.这里很棘手:你需要检查下一个字符是否存在('.'+4),如果它的值>=5,则转到4.1(否则转到5)
4.1.将字符串复制到左侧多留一个空格的新字符串(因为在"4"循环中9.9999将更改为10.0000),并将指针(P)设置为该字符串中的最后一个字符
4.2.如果*P在0到8之间,则添加1,然后转到5。
4.3.如果*P为9,则将其设置为零,将指针向左移动一(-1)并转到4.2
4.4.如果*P是".",将指针向左移动一(-1)并转到4.2
5.删除小数点右侧的所有0(如果需要,还可以自行删除小数点),就完成了
6.删除所有内容,并使用双重转换方法。。。

您可以使用递归来完成此操作。

void roundstr(std::string& strp) {
    char clast;
    char nlast;
    int sl(strp.length()), ipos(0);
    if (strp[sl - 1] == '.') { // skip the dot
        strp.erase(strp.end() - 1);
        nlast='.';
        roundstr(strp);
        return;
    }
    if (strp[sl - 2] == '.') { // handle dot is next char
        ipos = 1;
    }
    if (sl == 1) { // handle first char
        nlast = '0'; // virtual 0 in front of first char
        clast = strp[1];
    } else {
        clast = strp[sl - 1]; // current (last) char
        nlast = strp[sl - 2 - ipos]; // next to last char
    }
    if (clast >= '5') { // need to operate only if meet 5 or larger, otherwise stop
        nlast++;
        strp.erase(strp.end() - 1); // remove the last char from the string
        if (nlast == ':') { // if need to round further
            strp.replace(strp.end() - 1 - ipos, strp.end() - ipos, 1, nlast);
            clast = '0'; // keep current char value
            roundstr(strp);
        } else {
            strp.replace(strp.end() - 1 - ipos, strp.end() - ipos, 1, nlast);
            if(clast==':') clast = '0';
        }
    }
    sl = strp.length();
    if (nlast == ':') {
        if (ipos == 1) {
            strp += '.';
        } else {
            strp += '0';
        }
    } else if (nlast=='.' && strp[sl-1] != '.') {
        strp += '.';
    } else {
        if (ipos == 1 && strp[sl-1]!='.') {
            strp += '.';
        } else {
            strp += clast;
        }
    }
    return;
}

你这样称呼它:

#include <string>
#include <iostream>
#include <iterator>
#include <stdlib.h>
void roundstr(std::string&);
int main(int argc, char* argv[]) {
    int p;
    std::string strp;
    switch (argc) {
    case 2:
        p = 2;
        break;
    case 3:
        p = atoi(argv[2]);
        strp = argv[1];
        break;
    default:
        return 1;
        break;
    }
    std::cout << strp << " " << p << std::endl;
    roundstr(strp);
    strp.erase(strp.end()-p, strp.end());
    std::cout << strp << std::endl;
    return 0;
}

正如您所看到的,您必须处理删除多余的数字,但在构建过程中,编写一个包装函数,甚至一个类来自动完成这一操作并不困难。

如果使用c字符串而不是std::string,这也可能会更快。如果您计划进行重载符号运算,我建议用char *替换所有std::stringchar,但在删除和添加值时应更加小心。

在字符串中查找点。然后,找到该位置之后的第四个字符。将子字符串从0返回到点+3的位置,然后调整最后一个字符-如果原始字符串中点后的第四个字符大于或等于5,则递增最后一个字符串(或设置为"0",如果为"9",则递增prievus,依此类推)。如果小于5,则不执行任何操作,只返回原始字符串的子字符串。

尽管如此,我认为如果您只是将其转换为float,则不会影响性能,请使用(例如)boost::format将其取整。

这首先将其强制转换为浮点值。

int main()
{
    std::string f = "12345.567675";
    std::ostringstream ss;
    float x = boost::lexical_cast<float>(f);
    ss << std::fixed << std::setprecision(3);
    ss << x;
    std::string s = ss.str();
    std::cout << s << "n";
    return 0;
}

结果如下:

./a.out 
12345.567

假设您总是有一个点,并且在它之后至少有3位数字:

char *str = "12345.567675";
char *p = str;
while ( *p != '.' )
{
    printf( "%c", *p++ );
    // find a dot
}
for ( int i = 0; i < 4; i++ )
{
    printf( "%c", *p++ );
}