yas源码解析 之 number与字符串互转

我猜对于C++比较熟悉的小伙伴,对于C++中的std::stoi、std::stol、std::stoll、std::stodstd::stof和std::to_string都不陌生,它们共同完成字符串和数字之间的互转,使用起来非常方便,但是它们的实现大家未必真得有过研究,这不机会来了,yas已经通过非常直白的方式进行了实现,相信比大家直接去看C++源码要轻松的多,而且yas的代码也非常方便移植。

1.utoa 无符号整数转为字符串

template<typename T>
std::size_t default_traits::utoa(char *buf, const std::size_t, T v) {
	std::uint64_t l = __YAS_SCAST(std::uint64_t, v), n = l;
    
    // 计算 无符号整数 转为 字符串 需要字符个数
	std::size_t len = 1;
	if ( l >= 10000000000000000ull ) { len += 16; l /= 10000000000000000ull; }
	if ( l >= 100000000ull ) { len += 8; l /= 100000000ull; }
	if ( l >= 10000ull ) { len += 4; l /= 10000ull; }
	if ( l >= 100ull ) { len += 2; l /= 100ull; }
	if ( l >= 10ull ) { len += 1; }

    // 设置结束符"\0"
	*(buf+len) = 0;
	char *p = buf+len-1;
	switch ( len ) {
        case 20: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 19: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 18: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 17: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 16: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 15: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 14: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 13: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 12: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 11: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 10: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 9 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 8 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 7 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 6 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 5 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 4 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 3 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 2 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
        case 1 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10;
        default: break;
	}

	return len;
}

代码看着很吓人,实际原理很简单:

  1. 计算无符号整数转为字符串所需字符数组的大小
  2. 设置字符串结束字符“\0”
  3. 将整数的每一位转为字符,存到字符数组

注意:代码中的switch case中没有break,目的就是每一位依次进行处理

2. itoa 整数转字符串

template<typename T>
std::size_t default_traits::itoa(char *buf, const std::size_t, T v) {
	if ( v < 0 ) {
        *buf++ = '-';
        return 1 + default_traits::utoa(buf, 0/*unused*/, __YAS_SCAST(std::int64_t, std::abs(v)));
    }

    return default_traits::utoa(buf, 0/*unused*/, __YAS_SCAST(std::int64_t, v));
}

与utoa不同之处,就是需要考虑负号,如果,是负数,最终字符数组长度要多1;如果,是正数,直接依赖utoa的实现

3. atou 字符串转无符号整数

template<typename T>
T default_traits::atou(const char *str_, std::size_t size) {
	const std::uint8_t *str = __YAS_RCAST(const std::uint8_t *, str_);
	std::uint64_t v= 0;
	switch ( size ) {
        case 20: v = __YAS_SCAST(T, v+(str[size-20]-'0')*10000000000000000000ull); __YAS_FALLTHROUGH;
        case 19: v = __YAS_SCAST(T, v+(str[size-19]-'0')*1000000000000000000ull); __YAS_FALLTHROUGH;
        case 18: v = __YAS_SCAST(T, v+(str[size-18]-'0')*100000000000000000ull); __YAS_FALLTHROUGH;
        case 17: v = __YAS_SCAST(T, v+(str[size-17]-'0')*10000000000000000ull); __YAS_FALLTHROUGH;
        case 16: v = __YAS_SCAST(T, v+(str[size-16]-'0')*1000000000000000ull); __YAS_FALLTHROUGH;
        case 15: v = __YAS_SCAST(T, v+(str[size-15]-'0')*100000000000000ull); __YAS_FALLTHROUGH;
        case 14: v = __YAS_SCAST(T, v+(str[size-14]-'0')*10000000000000ull); __YAS_FALLTHROUGH;
        case 13: v = __YAS_SCAST(T, v+(str[size-13]-'0')*1000000000000ull); __YAS_FALLTHROUGH;
        case 12: v = __YAS_SCAST(T, v+(str[size-12]-'0')*100000000000ull); __YAS_FALLTHROUGH;
        case 11: v = __YAS_SCAST(T, v+(str[size-11]-'0')*10000000000ull); __YAS_FALLTHROUGH;
        case 10: v = __YAS_SCAST(T, v+(str[size-10]-'0')*1000000000ull); __YAS_FALLTHROUGH;
        case  9: v = __YAS_SCAST(T, v+(str[size- 9]-'0')*100000000ull); __YAS_FALLTHROUGH;
        case  8: v = __YAS_SCAST(T, v+(str[size- 8]-'0')*10000000ull); __YAS_FALLTHROUGH;
        case  7: v = __YAS_SCAST(T, v+(str[size- 7]-'0')*1000000ull); __YAS_FALLTHROUGH;
        case  6: v = __YAS_SCAST(T, v+(str[size- 6]-'0')*100000ull); __YAS_FALLTHROUGH;
        case  5: v = __YAS_SCAST(T, v+(str[size- 5]-'0')*10000ull); __YAS_FALLTHROUGH;
        case  4: v = __YAS_SCAST(T, v+(str[size- 4]-'0')*1000ull); __YAS_FALLTHROUGH;
        case  3: v = __YAS_SCAST(T, v+(str[size- 3]-'0')*100ull); __YAS_FALLTHROUGH;
        case  2: v = __YAS_SCAST(T, v+(str[size- 2]-'0')*10ull); __YAS_FALLTHROUGH;
        case  1: v = __YAS_SCAST(T, v+(str[size- 1]-'0')*1ull);
        default: break;
	}

	return __YAS_SCAST(T, v);
}

这里switch case中同样没有break,目的就是逐字符加权累加得到最终的无符号整数。

4. atoi 字符串转整型

template<typename T>
T default_traits::atoi(const char *str, std::size_t size) {
	if ( *str == '-' ) {
		++str;

        return -default_traits::atou<T>(str, size-1);
	}

    return default_traits::atou<T>(str, size);
}

如果字符串以"-"开头,表示是一个负数的字符串,剩下的就是依赖atou将字符串转为整数

5. atod 字符串转双精度浮点型

template<typename T>
T default_traits::atod(const char *str, std::size_t size) {
    static const double es[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
         1e+0  ,1e+1  ,  1e+2,  1e+3,  1e+4,  1e+5,  1e+6,  1e+7,  1e+8,  1e+9
        ,1e+10 ,1e+11 , 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19
        ,1e+20 ,1e+21 , 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29
        ,1e+30 ,1e+31 , 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39
        ,1e+40 ,1e+41 , 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49
        ,1e+50 ,1e+51 , 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59
        ,1e+60 ,1e+61 , 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69
        ,1e+70 ,1e+71 , 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79
        ,1e+80 ,1e+81 , 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89
        ,1e+90 ,1e+91 , 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99
        ,1e+100,1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109
        ,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119
        ,1e+120,1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129
        ,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139
        ,1e+140,1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149
        ,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159
        ,1e+160,1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169
        ,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179
        ,1e+180,1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189
        ,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199
        ,1e+200,1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209
        ,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219
        ,1e+220,1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229
        ,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239
        ,1e+240,1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249
        ,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259
        ,1e+260,1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269
        ,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279
        ,1e+280,1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289
        ,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299
        ,1e+300,1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
    };

	((void)size);
	T v = 0.0;
	bool neg = false;
	if ( *str == '-' ) {
		neg = true;
		++str;
	}
    // 整数部分解析
	for ( ; *str >= '0' && *str <= '9'; ++str) {
		v = __YAS_SCAST(T, (v*10.0) + (*str - '0'));
	}
    // 小数部分解析
	if ( *str == '.' ) {
		double f = 0.0;
		int n = 0;
		++str;
		for ( ; *str >= '0' && *str <= '9'; ++str, ++n) {
			f = (f*10.0) + (*str - '0');
		}
		v += __YAS_SCAST(T, f/es[n]);
	}

	v = neg ? -v : v;

	return v;
}

这个代码也是有点唬人,es[]这个数组也是够大的,但是,它是最容易看懂的。介绍一下代码的逻辑:

  1. 判断正负号
  2. 解析整数部分,每个循环通过x10进行加权
  3. 解析小数部分,每个循环通过x10进行加权,最后通过/es[n]加权
  4. 整数部分+小数部分
  5. 返回最终值

6. atof 字符串转单精度浮点型

template<typename T>
T default_traits::atof(const char *str, std::size_t size) {
	return default_traits::atod<T>(str, size);
}

实现部分就一行代码,直接依赖atod,原因就是atod更具有普遍性,能做到atod,那肯定能做到atof。

7. dtoa 双精度浮点数转字符串

template<typename T>
std::size_t default_traits::dtoa(char *buf, const std::size_t size, T v) {
    (void)size;

    return detail::rapidjson_dtoa(v, buf) - buf;
}

哈哈,这里作者偷懒了,直接依赖了rapidjson中的实现。

8. ftoa 单精度浮点数转字符串

template<typename T>
std::size_t default_traits::ftoa(char *buf, const std::size_t bufsize, T v) {
	return default_traits::dtoa(buf, bufsize, v);
}

这个没啥好说的,直接依赖dtoa就行了。

总结

看了上面的代码,相信大家对于数字与字符串之间的转换肯定有了新的认识,也不过如此是吗?我认为看起来虽然不难,但是能写出如此简洁的代码,是非常值得我们学习的。与诸君共勉!!!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值