Blog | QuasarDB

String to int and vice versa

Written by user | 15 May 2013

> Hope you guys are enjoying C++ Now!

We use Boost.Spirit for all our parsing and generation. What I mean by that is that when you have to parse input or generate output, we don’t use std::stringstream, we don’t use the C libraries and we definitely don’t write custom parsers or generators.

Using Boost.Spirit gives us a lot of security and although the learning curve can be steep, most of the parsers and generators are extremely simple.
One area where we don’t use Spirit is for log messages: we have so many log messages that we fear that switching to Spirit might lead to stratospheric compile times, despite our build grid.

To encourage good habits, we wrote a couple of simple wrappers, for example to transform a string to an int. This string conversion is the fastest you can get in C++. It outperforms the C libraries. It’s more secure. It’s just better. Don’t believe me? You should.

As part of our effort to open source some of our components, we’ve decided to add to our open source libraries our simple parsing wrappers. In this post I will show you how to use them. The good news is that you don’t need to know anything about Spirit to use them. Include the header, call the functions, and that's it!

Converting a string to integer

This is something you do extremely often in C++, would it be only for parameters validation. The library is very straightforward to use:

1
2
3
4
5
std :: string value =123;
int result = 0 ;
boost :: system :: errorcode ec = intfrom_string (value, result ) ;
assert ( !ec ) ;
assert (result 123 ) ;

The function will return an error if it cannot successfully parse the input.

Converting an integer to a string

The reciprocal function is even easier to use because there is no risk of the operation failing:

1
2
3
int v = 345 ;
const std :: string str = int tostring (v ) ;
assert (str "345" ) ;

We can compute, at compile time, how large the internal buffer should be. To make things easier we've specified a buffer large enough for 64-bit integers and don't try to optimize for smaller integers (it would however be easy to do, as we will see).

Given an integer, how many figures do you need to represent it as a decimal number? Let's use math!

(begin{align}
2^x = 10^y Leftrightarrow e^{ln(2^x)} = e^{ln(10^y)}
Leftrightarrow e^x = e^y frac{e^{ln(10)}}{e^{ln(2)}}
Leftrightarrow x = y frac{ln(10)}{ln(2)}
Leftrightarrow y = x frac{ln(2)}{ln(10)}
end{align}) In other words, for a 64-bit number, you will need (64 frac{ln(2)}{ln(10)}) figures, that's around 20. An easier to remember rule of thumb is to divide the exponent by 3.

If you add the minus sign you get one more char. You also need one more char for the terminating zero. That's 22. Let's round that to 32 because it might be easier to align.

As for the rest, if you look at the source code, you will see that the functions are direct wrappers to Spirit. We hope they will encourage you to play with Spirit and at the very least that they will be useful to you!